mirror of https://github.com/apache/jclouds.git
Issue 69: ensured redirect handlers copy the content stream before closing it, as this data is needed for error parsing
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1465 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
cf1067465b
commit
81ee88335e
|
@ -44,8 +44,8 @@ public class AWSResponseException extends HttpResponseException {
|
|||
private AWSError error = new AWSError();
|
||||
|
||||
public AWSResponseException(HttpFutureCommand<?> command, HttpResponse response, AWSError error) {
|
||||
super(String.format("command %1$s failed with error: %2$s", command.toString(), error
|
||||
.toString()), command, response);
|
||||
super(String.format("command %s failed with code %s, error: %s", command.toString(), response
|
||||
.getStatusCode(), error.toString()), command, response);
|
||||
this.setError(error);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,103 +29,104 @@ import java.util.Map;
|
|||
/**
|
||||
* When an Amazon S3 request is in error, the client receives an error response.
|
||||
*
|
||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?ErrorResponse.html" />
|
||||
* @see <a
|
||||
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?ErrorResponse.html"
|
||||
* />
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class AWSError {
|
||||
private String code;
|
||||
private String message;
|
||||
private String requestId;
|
||||
private String requestToken;
|
||||
private Map<String, String> details = new HashMap<String, String>();
|
||||
private String stringSigned;
|
||||
private String code;
|
||||
private String message;
|
||||
private String requestId;
|
||||
private String requestToken;
|
||||
private Map<String, String> details = new HashMap<String, String>();
|
||||
private String stringSigned;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("AWSError");
|
||||
sb.append("{code='").append(code).append('\'');
|
||||
sb.append(", message='").append(message).append('\'');
|
||||
sb.append(", requestId='").append(requestId).append('\'');
|
||||
sb.append(", requestToken='").append(requestToken).append('\'');
|
||||
if (stringSigned != null)
|
||||
sb.append(", stringSigned='").append(stringSigned).append('\'');
|
||||
sb.append(", context='").append(details.toString()).append('\'')
|
||||
.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("AWSError");
|
||||
sb.append("{requestId='").append(requestId).append('\'');
|
||||
sb.append(", requestToken='").append(requestToken).append('\'');
|
||||
if (code != null)
|
||||
sb.append(", code='").append(code).append('\'');
|
||||
if (message != null)
|
||||
sb.append(", message='").append(message).append('\'');
|
||||
if (stringSigned != null)
|
||||
sb.append(", stringSigned='").append(stringSigned).append('\'');
|
||||
if (details.size() != 0)
|
||||
sb.append(", context='").append(details.toString()).append('\'').append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* The error code is a string that uniquely identifies an error condition.
|
||||
* It is meant to be read and understood by programs that detect and handle
|
||||
* errors by type
|
||||
*
|
||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorCode.html" />
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
/**
|
||||
* The error code is a string that uniquely identifies an error condition. It is meant to be read
|
||||
* and understood by programs that detect and handle errors by type
|
||||
*
|
||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorCode.html" />
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* The error message contains a generic description of the error condition
|
||||
* in English.
|
||||
*
|
||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorMessage.html" />
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* The error message contains a generic description of the error condition in English.
|
||||
*
|
||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorMessage.html" />
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* * A unique ID assigned to each request by the system. In the unlikely
|
||||
* event that you have problems with Amazon S3, Amazon can use this to help
|
||||
* troubleshoot the problem.
|
||||
*
|
||||
*/
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
/**
|
||||
* * A unique ID assigned to each request by the system. In the unlikely event that you have
|
||||
* problems with Amazon S3, Amazon can use this to help troubleshoot the problem.
|
||||
*
|
||||
*/
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setStringSigned(String stringSigned) {
|
||||
this.stringSigned = stringSigned;
|
||||
}
|
||||
public void setStringSigned(String stringSigned) {
|
||||
this.stringSigned = stringSigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return what jclouds signed before sending the request.
|
||||
*/
|
||||
public String getStringSigned() {
|
||||
return stringSigned;
|
||||
}
|
||||
/**
|
||||
* @return what jclouds signed before sending the request.
|
||||
*/
|
||||
public String getStringSigned() {
|
||||
return stringSigned;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> context) {
|
||||
this.details = context;
|
||||
}
|
||||
public void setDetails(Map<String, String> context) {
|
||||
this.details = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return additional details surrounding the error.
|
||||
*/
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
}
|
||||
/**
|
||||
* @return additional details surrounding the error.
|
||||
*/
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setRequestToken(String requestToken) {
|
||||
this.requestToken = requestToken;
|
||||
}
|
||||
public void setRequestToken(String requestToken) {
|
||||
this.requestToken = requestToken;
|
||||
}
|
||||
|
||||
public String getRequestToken() {
|
||||
return requestToken;
|
||||
}
|
||||
public String getRequestToken() {
|
||||
return requestToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,8 @@ public class DeleteBucket extends S3FutureCommand<Boolean> {
|
|||
AWSResponseException responseException = (AWSResponseException) e.getCause();
|
||||
if (responseException.getResponse().getStatusCode() == 404) {
|
||||
return true;
|
||||
} else if ("BucketNotEmpty".equals(responseException.getError().getCode())) {
|
||||
} else if ("BucketNotEmpty".equals(responseException.getError().getCode())
|
||||
|| responseException.getResponse().getStatusCode() == 409) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.jclouds.http.HttpConstants;
|
|||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.http.annotation.ClientError;
|
||||
import org.jclouds.http.annotation.Redirection;
|
||||
import org.jclouds.http.annotation.ServerError;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
||||
|
@ -78,6 +79,8 @@ public class LiveS3ConnectionModule extends AbstractModule {
|
|||
}
|
||||
|
||||
protected void bindErrorHandlers() {
|
||||
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
|
||||
ParseAWSErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(
|
||||
ParseAWSErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(
|
||||
|
|
|
@ -23,20 +23,22 @@
|
|||
*/
|
||||
package org.jclouds.aws.s3.handlers;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jclouds.aws.AWSResponseException;
|
||||
import org.jclouds.aws.domain.AWSError;
|
||||
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
|
||||
import org.jclouds.aws.s3.reference.S3Headers;
|
||||
import org.jclouds.aws.s3.xml.S3ParserFactory;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpFutureCommand;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
|
@ -48,38 +50,51 @@ import com.google.inject.Inject;
|
|||
*
|
||||
*/
|
||||
public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
private final S3ParserFactory parserFactory;
|
||||
private final S3ParserFactory parserFactory;
|
||||
|
||||
@Inject
|
||||
public ParseAWSErrorFromXmlContent(S3ParserFactory parserFactory) {
|
||||
this.parserFactory = parserFactory;
|
||||
}
|
||||
@Inject
|
||||
public ParseAWSErrorFromXmlContent(S3ParserFactory parserFactory) {
|
||||
this.parserFactory = parserFactory;
|
||||
}
|
||||
|
||||
public void handleError(HttpFutureCommand<?> command, HttpResponse response) {
|
||||
AWSError error = new AWSError();
|
||||
error.setRequestId(response.getFirstHeaderOrNull(S3Headers.REQUEST_ID));
|
||||
error.setRequestToken(response
|
||||
.getFirstHeaderOrNull(S3Headers.REQUEST_TOKEN));
|
||||
InputStream errorStream = response.getContent();
|
||||
try {
|
||||
if (errorStream != null) {
|
||||
error = parserFactory.createErrorParser().parse(errorStream);
|
||||
if ("SignatureDoesNotMatch".equals(error.getCode()))
|
||||
error.setStringSigned(RequestAuthorizeSignature
|
||||
.createStringToSign(command.getRequest()));
|
||||
error.setRequestToken(response
|
||||
.getFirstHeaderOrNull(S3Headers.REQUEST_TOKEN));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn(e, "error parsing XML reponse: %1$s", response);
|
||||
} finally {
|
||||
command.setException(new AWSResponseException(command, response,
|
||||
error));
|
||||
IOUtils.closeQuietly(errorStream);
|
||||
}
|
||||
}
|
||||
public void handleError(HttpFutureCommand<?> command, HttpResponse response) {
|
||||
String content;
|
||||
try {
|
||||
content = response.getContent() != null ? Utils.toStringAndClose(response.getContent())
|
||||
: null;
|
||||
if (content != null) {
|
||||
try {
|
||||
if (content.indexOf('<') >= 0) {
|
||||
AWSError error = parseAWSErrorFromContent(command, response, content);
|
||||
command.setException(new AWSResponseException(command, response, error));
|
||||
} else {
|
||||
command.setException(new HttpResponseException(command, response, content));
|
||||
}
|
||||
} catch (Exception he) {
|
||||
command.setException(new HttpResponseException(command, response, content));
|
||||
Utils.rethrowIfRuntime(he);
|
||||
}
|
||||
} else {
|
||||
command.setException(new HttpResponseException(command, response));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
command.setException(new HttpResponseException(command, response));
|
||||
Utils.rethrowIfRuntime(e);
|
||||
}
|
||||
}
|
||||
|
||||
private AWSError parseAWSErrorFromContent(HttpFutureCommand<?> command, HttpResponse response,
|
||||
String content) throws HttpException {
|
||||
AWSError error = parserFactory.createErrorParser().parse(
|
||||
new ByteArrayInputStream(content.getBytes()));
|
||||
error.setRequestId(response.getFirstHeaderOrNull(S3Headers.REQUEST_ID));
|
||||
error.setRequestToken(response.getFirstHeaderOrNull(S3Headers.REQUEST_TOKEN));
|
||||
if ("SignatureDoesNotMatch".equals(error.getCode()))
|
||||
error.setStringSigned(RequestAuthorizeSignature.createStringToSign(command.getRequest()));
|
||||
return error;
|
||||
}
|
||||
|
||||
}
|
|
@ -59,9 +59,14 @@ public class FutureCommandConnectionPoolClient<E, C, O extends FutureCommand<E,
|
|||
poolMap = new MapMaker()
|
||||
.makeComputingMap(new Function<E, FutureCommandConnectionPool<E, C, O>>() {
|
||||
public FutureCommandConnectionPool<E, C, O> apply(E endPoint) {
|
||||
FutureCommandConnectionPool<E, C, O> pool = poolFactory.create(endPoint);
|
||||
addDependency(pool);
|
||||
return pool;
|
||||
try {
|
||||
FutureCommandConnectionPool<E, C, O> pool = poolFactory.create(endPoint);
|
||||
addDependency(pool);
|
||||
return pool;
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, "error creating entry for %s", endPoint);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.commandQueue = commandQueue;
|
||||
|
@ -124,7 +129,7 @@ public class FutureCommandConnectionPoolClient<E, C, O extends FutureCommand<E,
|
|||
exceptionIfNotActive();
|
||||
FutureCommandConnectionPool<E, C, O> pool = poolMap.get(command.getRequest().getEndPoint());
|
||||
if (pool == null) {
|
||||
//TODO limit;
|
||||
// TODO limit;
|
||||
logger.warn("pool not available for command %s; retrying", command);
|
||||
commandQueue.add(command);
|
||||
return;
|
||||
|
@ -138,7 +143,8 @@ public class FutureCommandConnectionPoolClient<E, C, O extends FutureCommand<E,
|
|||
commandQueue.add(command);
|
||||
return;
|
||||
} catch (TimeoutException e) {
|
||||
logger.warn(e, "Timeout getting a connection for command %s on pool %s; retrying", command, pool);
|
||||
logger.warn(e, "Timeout getting a connection for command %s on pool %s; retrying",
|
||||
command, pool);
|
||||
commandQueue.add(command);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
*/
|
||||
package org.jclouds.http.handlers;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -34,6 +38,7 @@ import org.jclouds.http.HttpResponse;
|
|||
import org.jclouds.http.HttpRetryHandler;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
|
@ -54,17 +59,49 @@ public class RedirectionRetryHandler implements HttpRetryHandler {
|
|||
}
|
||||
|
||||
public boolean shouldRetryRequest(HttpFutureCommand<?> command, HttpResponse response) {
|
||||
IOUtils.closeQuietly(response.getContent());
|
||||
closeConnectionButKeepContentStream(response);
|
||||
|
||||
command.incrementRedirectCount();
|
||||
|
||||
String hostHeader = response.getFirstHeaderOrNull(HttpHeaders.LOCATION);
|
||||
if (hostHeader != null && command.getRedirectCount() < retryCountLimit) {
|
||||
URI redirectURI = URI.create(hostHeader);
|
||||
command.getRequest().setEndPoint(redirectURI);
|
||||
URI endPoint = parseEndPoint(hostHeader);
|
||||
command.getRequest().setEndPoint(endPoint);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Content stream may need to be read. However, we should always close the http stream.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void closeConnectionButKeepContentStream(HttpResponse response) {
|
||||
if (response.getContent() != null) {
|
||||
try {
|
||||
byte[] data = IOUtils.toByteArray(response.getContent());
|
||||
response.setContent(new ByteArrayInputStream(data));
|
||||
} catch (IOException e) {
|
||||
logger.error(e, "Error consuming input");
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private URI parseEndPoint(String hostHeader) {
|
||||
URI redirectURI = URI.create(hostHeader);
|
||||
String scheme = redirectURI.getScheme();
|
||||
|
||||
checkState(redirectURI.getScheme().startsWith("http"), String.format(
|
||||
"header %s didn't parse an http scheme: [%s]", hostHeader, scheme));
|
||||
int port = redirectURI.getPort() > 0 ? redirectURI.getPort() : redirectURI.getScheme()
|
||||
.equals("https") ? 443 : 80;
|
||||
String host = redirectURI.getHost();
|
||||
checkState(!host.matches("[/]"), String.format(
|
||||
"header %s didn't parse an http host correctly: [%s]", hostHeader, host));
|
||||
URI endPoint = URI.create(String.format("%s://%s:%d", scheme, host, port));
|
||||
return endPoint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.jclouds.logging.Logger;
|
|||
|
||||
/**
|
||||
* // TODO: Adrian: Document this!
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class Utils {
|
||||
|
@ -45,135 +45,155 @@ public class Utils {
|
|||
@Resource
|
||||
protected static Logger logger = Logger.NULL;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <E> Exception type you'd like rethrown
|
||||
* @param e Exception you are inspecting
|
||||
* @throws E
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends Exception> void rethrowIfRuntimeOrSameType(Exception e) throws E {
|
||||
if (e instanceof ExecutionException) {
|
||||
Throwable nested = e.getCause();
|
||||
if (nested instanceof Error)
|
||||
throw (Error) nested;
|
||||
e = (Exception) nested;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param <E>
|
||||
* Exception type you'd like rethrown
|
||||
* @param e
|
||||
* Exception you are inspecting
|
||||
* @throws E
|
||||
*/
|
||||
public static void rethrowIfRuntime(Exception e) {
|
||||
if (e instanceof ExecutionException) {
|
||||
Throwable nested = e.getCause();
|
||||
if (nested instanceof Error)
|
||||
throw (Error) nested;
|
||||
e = (Exception) nested;
|
||||
}
|
||||
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
try {
|
||||
throw (E) e;
|
||||
} catch (ClassCastException throwAway) {
|
||||
// using cce as there's no way to do instanceof E in current java
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
}
|
||||
|
||||
public static String toStringAndClose(InputStream input) throws IOException {
|
||||
try {
|
||||
return IOUtils.toString(input);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param <E>
|
||||
* Exception type you'd like rethrown
|
||||
* @param e
|
||||
* Exception you are inspecting
|
||||
* @throws E
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends Exception> void rethrowIfRuntimeOrSameType(Exception e) throws E {
|
||||
if (e instanceof ExecutionException) {
|
||||
Throwable nested = e.getCause();
|
||||
if (nested instanceof Error)
|
||||
throw (Error) nested;
|
||||
e = (Exception) nested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given string with the given encoding, if possible.
|
||||
* If the encoding fails with {@link UnsupportedEncodingException},
|
||||
* log a warning and fall back to the system's default encoding.
|
||||
*
|
||||
* @see {@link String#getBytes(String)}
|
||||
* @see {@link String#getBytes()} - used as fall-back.
|
||||
*
|
||||
* @param str
|
||||
* @param encoding
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encodeString(String str, String encoding) {
|
||||
try {
|
||||
return str.getBytes(encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn(e, "Failed to encode string to bytes with encoding " + encoding
|
||||
+ ". Falling back to system's default encoding");
|
||||
return str.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given string with the UTF-8 encoding, the sane default.
|
||||
* In the very unlikely event the encoding fails with
|
||||
* {@link UnsupportedEncodingException}, log a warning and fall back
|
||||
* to the system's default encoding.
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encodeString(String str) {
|
||||
return encodeString(str, UTF8_ENCODING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given string with the given encoding, if possible.
|
||||
* If the decoding fails with {@link UnsupportedEncodingException},
|
||||
* log a warning and fall back to the system's default encoding.
|
||||
*
|
||||
* @param bytes
|
||||
* @param encoding
|
||||
* @return
|
||||
*/
|
||||
public static String decodeString(byte[] bytes, String encoding) {
|
||||
try {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
try {
|
||||
throw (E) e;
|
||||
} catch (ClassCastException throwAway) {
|
||||
// using cce as there's no way to do instanceof E in current java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String toStringAndClose(InputStream input) throws IOException {
|
||||
try {
|
||||
return IOUtils.toString(input);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given string with the given encoding, if possible. If the encoding fails with
|
||||
* {@link UnsupportedEncodingException}, log a warning and fall back to the system's default
|
||||
* encoding.
|
||||
*
|
||||
* @see {@link String#getBytes(String)}
|
||||
* @see {@link String#getBytes()} - used as fall-back.
|
||||
*
|
||||
* @param str
|
||||
* @param encoding
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encodeString(String str, String encoding) {
|
||||
try {
|
||||
return str.getBytes(encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn(e, "Failed to encode string to bytes with encoding " + encoding
|
||||
+ ". Falling back to system's default encoding");
|
||||
return str.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given string with the UTF-8 encoding, the sane default. In the very unlikely event
|
||||
* the encoding fails with {@link UnsupportedEncodingException}, log a warning and fall back to
|
||||
* the system's default encoding.
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encodeString(String str) {
|
||||
return encodeString(str, UTF8_ENCODING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given string with the given encoding, if possible. If the decoding fails with
|
||||
* {@link UnsupportedEncodingException}, log a warning and fall back to the system's default
|
||||
* encoding.
|
||||
*
|
||||
* @param bytes
|
||||
* @param encoding
|
||||
* @return
|
||||
*/
|
||||
public static String decodeString(byte[] bytes, String encoding) {
|
||||
try {
|
||||
return new String(bytes, encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn(e, "Failed to decode bytes to string with encoding " + encoding
|
||||
+ ". Falling back to system's default encoding");
|
||||
+ ". Falling back to system's default encoding");
|
||||
return new String(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given string with the UTF-8 encoding, the sane default.
|
||||
* In the very unlikely event the encoding fails with
|
||||
* {@link UnsupportedEncodingException}, log a warning and fall back
|
||||
* to the system's default encoding.
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String decodeString(byte[] bytes) {
|
||||
return decodeString(bytes, UTF8_ENCODING);
|
||||
}
|
||||
/**
|
||||
* Decode the given string with the UTF-8 encoding, the sane default. In the very unlikely event
|
||||
* the encoding fails with {@link UnsupportedEncodingException}, log a warning and fall back to
|
||||
* the system's default encoding.
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String decodeString(byte[] bytes) {
|
||||
return decodeString(bytes, UTF8_ENCODING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a path portion of a URI using the UTF-8 encoding, but leave slash '/'
|
||||
* characters and any parameters (anything after the first '?') un-encoded.
|
||||
* If encoding with UTF-8 fails, the method falls back to using the
|
||||
* system's default encoding.
|
||||
*
|
||||
* @param uri
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String encodeUriPath(String uri) {
|
||||
String path, params = "";
|
||||
|
||||
int offset;
|
||||
if ((offset = uri.indexOf('?')) >= 0) {
|
||||
path = uri.substring(0, offset);
|
||||
params = uri.substring(offset);
|
||||
} else {
|
||||
path = uri;
|
||||
}
|
||||
|
||||
String encodedUri;
|
||||
try {
|
||||
encodedUri = URLEncoder.encode(path, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
encodedUri = URLEncoder.encode(path);
|
||||
}
|
||||
return encodedUri.replace("%2F", "/") + params;
|
||||
}
|
||||
/**
|
||||
* Encode a path portion of a URI using the UTF-8 encoding, but leave slash '/' characters and
|
||||
* any parameters (anything after the first '?') un-encoded. If encoding with UTF-8 fails, the
|
||||
* method falls back to using the system's default encoding.
|
||||
*
|
||||
* @param uri
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String encodeUriPath(String uri) {
|
||||
String path, params = "";
|
||||
|
||||
int offset;
|
||||
if ((offset = uri.indexOf('?')) >= 0) {
|
||||
path = uri.substring(0, offset);
|
||||
params = uri.substring(offset);
|
||||
} else {
|
||||
path = uri;
|
||||
}
|
||||
|
||||
String encodedUri;
|
||||
try {
|
||||
encodedUri = URLEncoder.encode(path, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
encodedUri = URLEncoder.encode(path);
|
||||
}
|
||||
return encodedUri.replace("%2F", "/") + params;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -155,7 +155,8 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient<HTTPReque
|
|||
|
||||
private void followRedirectsUnlessRequestContainsPayload(HttpRequest request,
|
||||
FetchOptions options) {
|
||||
if (request.getPayload() != null)
|
||||
if (request.getPayload() != null || request.getMethod().equals(HTTPMethod.PUT)
|
||||
|| request.getMethod().equals(HTTPMethod.POST))
|
||||
options.doNotFollowRedirects();
|
||||
else
|
||||
options.followRedirects();
|
||||
|
|
Loading…
Reference in New Issue