diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/SharedKeyFilter.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/SharedKeyFilter.java index ba12447147c8..7e2e95bb88f9 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/SharedKeyFilter.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/SharedKeyFilter.java @@ -77,7 +77,7 @@ public void sign(ClientRequest cr) { private String getCanonicalizedResource(ClientRequest cr) { String result = "/" + this.getAccountName(); - result += cr.getURI().getPath(); + result += cr.getURI().getRawPath(); List queryParams = SharedKeyUtils.getQueryParams(cr.getURI().getQuery()); for (QueryParam p : queryParams) { diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/TableRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/TableRestProxy.java index c82b1429bde4..82b1270a9fb1 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/TableRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/TableRestProxy.java @@ -18,7 +18,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -162,7 +164,17 @@ private List encodeODataURIValues(List values) { } private String getEntityPath(String table, String partitionKey, String rowKey) { - return table + "(" + "PartitionKey='" + partitionKey + "',RowKey='" + rowKey + "')"; + return table + "(" + "PartitionKey='" + safeEncode(partitionKey) + "',RowKey='" + safeEncode(rowKey) + "')"; + } + + private String safeEncode(String input) { + String fixSingleQuotes = input.replace("'", "''"); + try { + return URLEncoder.encode(fixSingleQuotes, "UTF-8").replace("+", "%20"); + } + catch (UnsupportedEncodingException e) { + return fixSingleQuotes; + } } private WebResource addOptionalQueryParam(WebResource webResource, String key, Object value) { diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java index ec4667faee21..daf68d549f11 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java @@ -446,6 +446,53 @@ public void deleteEntityWorks() throws Exception { // Assert } + @Test + public void deleteEntityTroublesomeKeyWorks() throws Exception { + System.out.println("deleteEntityTroublesomeKeyWorks()"); + + // Arrange + Configuration config = createConfiguration(); + TableContract service = TableService.create(config); + Entity entity1 = new Entity().setPartitionKey("001").setRowKey("key with spaces"); + Entity entity2 = new Entity().setPartitionKey("001").setRowKey("key'with'quotes"); + Entity entity3 = new Entity().setPartitionKey("001").setRowKey("keyWithUnicode \uB2E4"); + Entity entity4 = new Entity().setPartitionKey("001").setRowKey("key 'with'' \uB2E4"); + + // Act + InsertEntityResult result1 = service.insertEntity(TEST_TABLE_2, entity1); + InsertEntityResult result2 = service.insertEntity(TEST_TABLE_2, entity2); + InsertEntityResult result3 = service.insertEntity(TEST_TABLE_2, entity3); + InsertEntityResult result4 = service.insertEntity(TEST_TABLE_2, entity4); + + service.deleteEntity(TEST_TABLE_2, result1.getEntity().getPartitionKey(), result1.getEntity().getRowKey()); + service.deleteEntity(TEST_TABLE_2, result2.getEntity().getPartitionKey(), result2.getEntity().getRowKey()); + service.deleteEntity(TEST_TABLE_2, result3.getEntity().getPartitionKey(), result3.getEntity().getRowKey()); + service.deleteEntity(TEST_TABLE_2, result4.getEntity().getPartitionKey(), result4.getEntity().getRowKey()); + + // Assert + try { + service.getEntity(TEST_TABLE_2, result1.getEntity().getPartitionKey(), result1.getEntity().getRowKey()); + assertFalse("Expect an exception when getting an entity that does not exist", true); + } + catch (ServiceException e) { + assertEquals("expect getHttpStatusCode", 404, e.getHttpStatusCode()); + + } + + QueryEntitiesResult assertResult2 = service.queryEntities( + TEST_TABLE_2, + new QueryEntitiesOptions().setQuery(new Query().setFilter(Filter.eq(Filter.litteral("RowKey"), + Filter.constant("key'with'quotes"))))); + + assertEquals(0, assertResult2.getEntities().size()); + + QueryEntitiesResult assertResult3 = service.queryEntities(TEST_TABLE_2); + for (Entity entity : assertResult3.getEntities()) { + assertFalse("Entity3 should be removed from the table", entity3.getRowKey().equals(entity.getRowKey())); + assertFalse("Entity4 should be removed from the table", entity4.getRowKey().equals(entity.getRowKey())); + } + } + @Test public void deleteEntityWithETagWorks() throws Exception { System.out.println("deleteEntityWithETagWorks()");