From 4403887200894480353482f1b171b9eaca169e0c Mon Sep 17 00:00:00 2001 From: jamurty Date: Fri, 29 May 2009 22:49:12 +0000 Subject: [PATCH] Fixed HttpRequest to work with non-ascii object names. Also added string encode/decode methods to the core Utils, please use this instead of the dreaded String#getBytes git-svn-id: http://jclouds.googlecode.com/svn/trunk@878 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../java/org/jclouds/aws/util/AWSUtils.java | 4 +- .../java/org/jclouds/http/HttpRequest.java | 33 ++++++++- .../src/main/java/org/jclouds/util/Utils.java | 74 +++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/aws/core/src/main/java/org/jclouds/aws/util/AWSUtils.java b/aws/core/src/main/java/org/jclouds/aws/util/AWSUtils.java index 58433afc64..9b2406d31d 100644 --- a/aws/core/src/main/java/org/jclouds/aws/util/AWSUtils.java +++ b/aws/core/src/main/java/org/jclouds/aws/util/AWSUtils.java @@ -86,7 +86,7 @@ public class AWSUtils extends Utils { private static String hmacBase64(String toEncode, byte[] key, Digest digest) { HMac hmac = new HMac(digest); byte[] resBuf = new byte[hmac.getMacSize()]; - byte[] plainBytes = toEncode.getBytes(); + byte[] plainBytes = Utils.encodeString(toEncode); byte[] keyBytes = key; hmac.init(new KeyParameter(keyBytes)); hmac.update(plainBytes, 0, plainBytes.length); @@ -142,5 +142,5 @@ public class AWSUtils extends Utils { public static String toBase64String(byte[] resBuf) { return new String(Base64.encode(resBuf)); - } + } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/http/HttpRequest.java b/core/src/main/java/org/jclouds/http/HttpRequest.java index 6f1fd9a68f..58cd71fa08 100644 --- a/core/src/main/java/org/jclouds/http/HttpRequest.java +++ b/core/src/main/java/org/jclouds/http/HttpRequest.java @@ -26,6 +26,8 @@ package org.jclouds.http; import static com.google.common.base.Preconditions.checkNotNull; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import javax.annotation.Resource; @@ -48,7 +50,7 @@ public class HttpRequest extends HttpMessage { public HttpRequest(String method, String uri) { this.method = checkNotNull(method, "method"); - this.uri = checkNotNull(uri, "uri"); + this.uri = encodeUri(checkNotNull(uri, "uri")); } @Override @@ -87,5 +89,34 @@ public class HttpRequest extends HttpMessage { public void setPayload(Object content) { this.payload = content; } + + /** + * Encode a path portion of a URI using the UTF-8 encoding, but leave slash '/' + * characters and any parameters (anything after the first '?') unencoded. + * If encoding with UTF-8 fails, the method falls back to using the + * system's default encoding. + * + * @param uri + * @return + */ + protected String encodeUri(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; + } } diff --git a/core/src/main/java/org/jclouds/util/Utils.java b/core/src/main/java/org/jclouds/util/Utils.java index 47e0a4b041..945c3f1e52 100644 --- a/core/src/main/java/org/jclouds/util/Utils.java +++ b/core/src/main/java/org/jclouds/util/Utils.java @@ -25,9 +25,13 @@ package org.jclouds.util; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.util.concurrent.ExecutionException; +import javax.annotation.Resource; + import org.apache.commons.io.IOUtils; +import org.jclouds.logging.Logger; /** * // TODO: Adrian: Document this! @@ -35,7 +39,10 @@ import org.apache.commons.io.IOUtils; * @author Adrian Cole */ public class Utils { + public static final String UTF8_ENCODING = "UTF-8"; + @Resource + protected static Logger logger = Logger.NULL; /** * @@ -71,4 +78,71 @@ public class Utils { } } + /** + * 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"); + 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); + } + }