Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #511 from rickle-msft/blobSasHeaders
Browse files Browse the repository at this point in the history
Added preserveRawValue option to SharedAccessHeaders
  • Loading branch information
rickle-msft authored Dec 2, 2019
2 parents 2b87018 + e91a622 commit 71acd88
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 19 deletions.
3 changes: 2 additions & 1 deletion ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
2019.08.15 Version 8.5.0
2019.12.02 Version 8.5.0
* Support for HTTP proxy with Basic auth.
* Support for HTTP proxy with Digest auth.
* Added an option to SharedAccessHeaders that will allow the customer to preserve the raw value set on the object. Headers could previously be changed by an internal url decode that might modified the desired value.

2019.08.05 Version 8.4.0
* Support for 2019-02-02 REST version. Please see our REST API documentation and blogs for information about the related added features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,47 @@ public void eventOccurred(SendingRequestEvent eventArg) {
sasBlob.download(new ByteArrayOutputStream(), null, null, context);
}

@Test
@Category(SlowTests.class)
public void testBlobSaSWithSharedAccessBlobHeadersPreserveRaw() throws InvalidKeyException,
IllegalArgumentException, StorageException, URISyntaxException, InterruptedException {
SharedAccessBlobPolicy sp = createSharedAccessPolicy(EnumSet.of(SharedAccessBlobPermissions.READ,
SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.LIST), 300);
BlobContainerPermissions perms = new BlobContainerPermissions();

perms.getSharedAccessPolicies().put("readperm", sp);
this.container.uploadPermissions(perms);
Thread.sleep(30000);

SharedAccessBlobHeaders headers = new SharedAccessBlobHeaders(true);
headers.setCacheControl("no%20cache");
headers.setContentDisposition("inline; filename=\"My Image.jpg\"; filename*=UTF-8''My%20Image.jpg");
headers.setContentEncoding("gzip%20");
headers.setContentLanguage("da%20");
headers.setContentType("text/html; charset=utf%208");

CloudBlockBlob sasBlob = new CloudBlockBlob(new URI(this.blob.getUri().toString() + "?"
+ this.blob.generateSharedAccessSignature(null, headers, "readperm")));
OperationContext context = new OperationContext();

context.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() {

@Override
public void eventOccurred(SendingRequestEvent eventArg) {
HttpURLConnection connection = (HttpURLConnection) eventArg.getConnectionObject();
assertEquals("no%20cache", connection.getHeaderField(Constants.HeaderConstants.CACHE_CONTROL));
assertEquals("inline; filename=\"My Image.jpg\"; filename*=UTF-8''My%20Image.jpg",
connection.getHeaderField(Constants.HeaderConstants.CONTENT_DISPOSITION));
assertEquals("gzip%20", connection.getHeaderField(Constants.HeaderConstants.CONTENT_ENCODING));
assertEquals("da%20", connection.getHeaderField(Constants.HeaderConstants.CONTENT_LANGUAGE));
assertEquals("text/html; charset=utf%208",
connection.getHeaderField(Constants.HeaderConstants.CONTENT_TYPE));
}
});

sasBlob.download(new ByteArrayOutputStream(), null, null, context);
}

@Test
public void testAppendBlobCopyWithSasAndSnapshot()
throws URISyntaxException, StorageException, InterruptedException, IOException, InvalidKeyException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Copyright Microsoft Corporation
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -20,6 +20,8 @@
* RESERVED FOR INTERNAL USE. Represents the optional headers that can be returned using SAS.
*/
public abstract class SharedAccessHeaders {
private final boolean preserveRawValue;

/**
* The cache-control header returned.
*/
Expand Down Expand Up @@ -49,11 +51,31 @@ public abstract class SharedAccessHeaders {
* Initializes a new instance of the {@link SharedAccessHeaders} class.
*/
public SharedAccessHeaders() {
this(false);
}

/**
* Initializes a new instance of the {@link SharedAccessHeaders} class. The empty constructor should be preferred
* and this option should only be used by customers who are sure they do not want the safety usually afforded by
* this SDK when constructing a sas.
* <p>
* The header values are typically decoded before building the sas token. This can cause problems if the desired
* value for one of the headers contains something that looks like encoding. Setting this flag to true will ensure
* that the value of these headers are preserved as set on this object when constructing the sas.
* <p>
* Note that these values are preserved by encoding them here so that the decoding which happens at sas construction
* time returns them to the original values. So if get is called on this object when preserveRawValues was set to
* true, the value returned will be percent encoded.
*
* @param preserveRawValue Whether the sdk should preserve the raw value of these headers.
*/
public SharedAccessHeaders(boolean preserveRawValue) {
this.preserveRawValue = preserveRawValue;
}

/**
* Initializes a new instance of the {@link SharedAccessHeaders} class based on an existing instance.
*
*
* @param other
* A {@link SharedAccessHeaders} object which specifies the set of properties to clone.
*/
Expand All @@ -65,11 +87,13 @@ public SharedAccessHeaders(SharedAccessHeaders other) {
this.contentEncoding = other.contentEncoding;
this.contentLanguage = other.contentLanguage;
this.cacheControl = other.cacheControl;

this.preserveRawValue = other.preserveRawValue;
}

/**
* Gets the cache control header.
*
*
* @return A <code>String</code> which represents the cache control header.
*/
public String getCacheControl() {
Expand All @@ -78,17 +102,26 @@ public String getCacheControl() {

/**
* Sets the cache control header.
*
*
* @param cacheControl
* A <code>String</code> which specifies the cache control header.
*/
public void setCacheControl(String cacheControl) {
this.cacheControl = cacheControl;
if (this.preserveRawValue) {
try {
this.cacheControl = Utility.safeEncode(cacheControl);
} catch (StorageException e) {
e.printStackTrace();
}
}
else {
this.cacheControl = cacheControl;
}
}

/**
* Gets the content disposition header.
*
*
* @return A <code>String</code> which represents the content disposition header.
*/
public String getContentDisposition() {
Expand All @@ -97,17 +130,26 @@ public String getContentDisposition() {

/**
* Sets the content disposition header.
*
*
* @param contentDisposition
* A <code>String</code> which specifies the content disposition header.
*/
public void setContentDisposition(String contentDisposition) {
this.contentDisposition = contentDisposition;
if (this.preserveRawValue) {
try {
this.contentDisposition = Utility.safeEncode(contentDisposition);
} catch (StorageException e) {
e.printStackTrace();
}
}
else {
this.contentDisposition = contentDisposition;
}
}

/**
* Gets the content encoding header.
*
*
* @return A <code>String</code> which represents the content encoding header.
*/
public String getContentEncoding() {
Expand All @@ -116,17 +158,26 @@ public String getContentEncoding() {

/**
* Sets the content encoding header.
*
*
* @param contentEncoding
* A <code>String</code> which specifies the content encoding header.
*/
public void setContentEncoding(String contentEncoding) {
this.contentEncoding = contentEncoding;
if (this.preserveRawValue) {
try {
this.contentEncoding = Utility.safeEncode(contentEncoding);
} catch (StorageException e) {
e.printStackTrace();
}
}
else {
this.contentEncoding = contentEncoding;
}
}

/**
* Gets the content language header.
*
*
* @return A <code>String</code> which represents the content language header.
*/
public String getContentLanguage() {
Expand All @@ -135,17 +186,26 @@ public String getContentLanguage() {

/**
* Sets the content language header.
*
*
* @param contentLanguage
* A <code>String</code> which specifies the content language header.
*/
public void setContentLanguage(String contentLanguage) {
this.contentLanguage = contentLanguage;
if (this.preserveRawValue) {
try {
this.contentLanguage = Utility.safeEncode(contentLanguage);
} catch (StorageException e) {
e.printStackTrace();
}
}
else {
this.contentLanguage = contentLanguage;
}
}

/**
* Gets the content type header.
*
*
* @return A <code>String</code> which represents the content type header.
*/
public String getContentType() {
Expand All @@ -154,11 +214,21 @@ public String getContentType() {

/**
* Sets the content type header.
*
*
* @param contentType
* A <code>String</code> which specifies the content type header.
*/
public void setContentType(String contentType) {
this.contentType = contentType;
if (this.preserveRawValue) {
try {
this.contentType = Utility.safeEncode(contentType);
} catch (StorageException e) {
e.printStackTrace();
}
}
else {
this.contentType = contentType;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,27 @@ public final class SharedAccessBlobHeaders extends SharedAccessHeaders {
* Initializes a new instance of the {@link SharedAccessBlobHeaders} class.
*/
public SharedAccessBlobHeaders() {
super();
}

/**
* Initializes a new instance of the {@link SharedAccessHeaders} class. The empty constructor should be preferred
* and this option should only be used by customers who are sure they do not want the safety usually afforded by
* this SDK when constructing a sas.
* <p>
* The header values are typically decoded before building the sas token. This can cause problems if the desired
* value for one of the headers contains something that looks like encoding. Setting this flag to true will ensure
* that the value of these headers are preserved as set on this object when constructing the sas.
* <p>
* Note that these values are preserved by encoding them here so that the decoding which happens at sas construction
* time returns them to the original values. So if get is called on this object when preserveRawValues was set to
* true, the value returned will be percent encoded.
*
* @param preserveRawValue Whether the sdk should preserve the raw value of these headers.
*/
public SharedAccessBlobHeaders(boolean preserveRawValue) {
super(preserveRawValue);
}
/**
* Initializes a new instance of the {@link SharedAccessBlobHeaders} class based on an existing instance.
*
Expand Down

0 comments on commit 71acd88

Please sign in to comment.