var blobServiceClient = new BlobServiceClient(connectionString, options);
var blobContainerClient = blobServiceClient.GetBlobContainerClient("somecontainer");
var pageBlobClient = blobContainerClient.GetPageBlobClient("someblob");
await blobContainerClient.CreateIfNotExistsAsync(cancellationToken: default);
await pageBlobClient.CreateIfNotExistsAsync(0, cancellationToken: default);
pageBlobClient.UploadPages(new MemoryStream(Encoding.ASCII.GetBytes("Test")), 0);
--------------- The above code throws the below error ----------------------------------
The value for one of the HTTP headers is not in the correct format.
RequestId:0761d774-b01e-0002-6a3a-a256ea000000
Time:2022-07-28T04:25:28.8145993Z
Status: 400 (The value for one of the HTTP headers is not in the correct format.)
ErrorCode: InvalidHeaderValue
Additional Information:
HeaderName: Range
HeaderValue: bytes=0-3
Content:
InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.
RequestId:0761d774-b01e-0002-6a3a-a256ea000000
Time:2022-07-28T04:25:28.8145993ZRangebytes=0-3
Headers:
Server: Windows-Azure-Blob/1.0,Microsoft-HTTPAPI/2.0
x-ms-request-id: 0761d774-b01e-0002-6a3a-a256ea000000
x-ms-client-request-id: ec21d32d-74ec-4942-9321-341da37e7a63
x-ms-version: 2021-08-06
x-ms-error-code: InvalidHeaderValue
Date: Thu, 28 Jul 2022 04:25:27 GMT
Content-Length: 320
Content-Type: application/xml
The reason you are getting this error is because the page size should be a multiple of 512 bytes and you are only uploading 4 bytes.
From this link (range header description):
Given that pages must be aligned with 512-byte boundaries, the start
offset must be a modulus of 512 and the end offset must be a modulus
of 512 – 1. Examples of valid byte ranges are 0-511, 512-1023, etc.
Related
I am experiencing a problem when working with CAST web receiver. DRM Video does not start on the TV (Android TV). Preloader works endlessly. How to solve this problem?
The title and description in the player is shown.
The logs show errors:
"shakaErrorCode":6007
LICENSE_REQUEST_FAILED - 6007 - The license request failed. This could be a timeout, a network failure, or a rejection by the server. error.data[0] is a shaka.util.Error from the networking engine. (https://shaka-player-demo.appspot.com/docs/api/shaka.util.Error.html)
"detailedErrorCode":905
LOAD_FAILED - 905 - A load command failed. - Verify the load request is set up properly and the media is able to play. (https://developers.google.com/cast/docs/web_receiver/error_codes?hl=en)
Error 6007 indicates problems with the license server. We checked the request parameters on another player and the DRM video starts. This does not work in cast receiver.
The problem occurs on the physical device
Sender: Redmi Note 8 Pro
Device with chromecast: Android Box
Chromecast built-in: 1.56.276477
CAST SDK CAF Version: 3.0.0103
API Request
Request URL: https://widevine-license.vudrm.tech/proxy
Request Method: POST
Status Code: 200 OK
Remote Address: XXX
Referrer Policy: strict-origin-when-cross-origin
Response Headers
access-control-allow-headers: Content-Type,X-Vudrm-Token
access-control-allow-methods: GET,POST,OPTIONS
access-control-allow-origin: *
Connection: keep-alive
Content-Length: 716
content-type: application/octet-stream
date: Mon, 13 Jun 2022 07:59:00 GMT
server: istio-envoy
x-b3-parentspanid: 3d92950cdf3d42ca
x-b3-sampled: 0
x-b3-spanid: 46ff3c9c3e815372
x-b3-traceid: XXX
x-envoy-upstream-healthchecked-cluster: widevine-license.default
x-envoy-upstream-service-time: 18
x-forwarded-for: XXX,XXX
x-request-id: XXX
x-vudrm: app=widevine-proxy; version=2.4.1
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US
CAST-DEVICE-CAPABILITIES: {"bluetooth_supported":false,"display_supported":true,"hi_res_audio_supported":false,"remote_control_input_supported":false,"touch_input_supported":false}
Connection: keep-alive
Content-Length: 297
content-type: application/json
Host: widevine-license.vudrm.tech
Origin: XXX
Referer: XXX
sec-ch-ua
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Linux; Android 9.0; Build/PI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000
Request Payload
{"token":"XXX","drm_info":[8,4],"contentId":"VS-ADULTSWIM"}
receiver.js
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();
const ContentType = {
DASH: 'application/dash+xml',
HLS: 'application/x-mpegurl'
};
const mediaFormatID = {
DASH: 2,
HLS: 4
};
// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';
// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);
// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
};
// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
LOG_TAG: cast.framework.LoggerLevel.DEBUG,
};
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
return new Promise((resolve, _reject) => {
// Configure player to parse DASH content
if (request.media.metadata.mediaFormatID == mediaFormatID.DASH) {
request.media.contentUrl = request.media.contentUrl;
request.media.contentType = ContentType.DASH;
// Customize the license url for playback
if (request.media.metadata.licenseUrl) {
playbackConfig.licenseUrl = request.media.metadata.licenseUrl;
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
let token = request.media.metadata.token;
let contentId = request.media.metadata.contentId;
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = false;
let body = {
token: token,
drm_info: Array.apply(null, new Uint8Array(requestInfo.content)),
contentId: contentId
};
body = JSON.stringify(body);
requestInfo.content = body;
requestInfo.headers["Content-Type"] = "application/json";
};
}
mpegDashStreamUrl = request.media.contentUrl;
vudrmToken = request.media.metadata.licenseUrl;
} else {
request.media.contentType = ContentType.HLS;
request.media.contentUrl = request.media.contentUrl;
request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
}
// Add metadata
let metadata = new cast.framework.messages.GenericMediaMetadata();
metadata.title = request.media.metadata.title;
metadata.subtitle = request.media.metadata.subtitle;
request.media.metadata = metadata;
resolve(request);
});
});
context.start({playbackConfig: playbackConfig});
There are 3 handlers that are used by the Shaka response filters -
PlaybackConfig#manifestHandler
PlaybackConfig#licenseHandler
PlaybackConfig#segmentHandler
I added this code and the error was solved:
playbackConfig.licenseHandler = data => {
return new Promise((resolve, _reject) => {
resolve(new Uint8Array(data));
});
};
I am trying to upload base64 string as image file to Azure Blob Storage. Using https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob documentation tried to create blob.
Request Syntax:
PUT https://myaccount.blob.core.windows.net/mycontainer/myblockblob HTTP/1.1
Request Headers:
x-ms-version: 2015-02-21
x-ms-date: <date>
Content-Type: text/plain; charset=UTF-8
x-ms-blob-content-disposition: attachment; filename="fname.ext"
x-ms-blob-type: BlockBlob
x-ms-meta-m1: v1
x-ms-meta-m2: v2
Authorization: SharedKey myaccount:YhuFJjN4fAR8/AmBrqBz7MG2uFinQ4rkh4dscbj598g=
Content-Length: 11
Request Body:
hello world
I am getting response as below,
<?xml
version="1.0" encoding="utf-8"?>
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:a5d32623-f01e-0040-4275-c1880d000000
Time:2020-11-23T08:45:49.6994297Z</Message>
<AuthenticationErrorDetail>The MAC signature found in the HTTP request 'YhuFJjN4fAR8/AmBrqBz7MG2uFinQ4rkh4dscbj598g=' is not the same as any computed signature. Server used following string to sign: 'PUT
11
text/plain; charset=UTF-8
x-ms-blob-content-disposition:attachment; filename="demo.txt"
x-ms-blob-type:BlockBlob
x-ms-date:Mon, 23 Nov 2020 13:08:11 GMT
x-ms-encryption-key:YhuFJjN4fAR8/AmBrqBz7MG2uFinQ4rkh4dscbj598g=
x-ms-meta-m1:v1
x-ms-meta-m2:v2
x-ms-version:2015-02-21
/<myaccount>/<mycontainer>/<myblob>'.</AuthenticationErrorDetail>
</Error>
How to resolve this issue?
A simple way to upload a blob is to use the sas token.
Nav to azure portal -> your storage account -> Shared access signature, then select the following options in the screenshot -> then click the Generate SAS and connection string button. The screenshot is as below:
Then copy the SAS token, and append it to the url. Then the new url looks like this: https://myaccount.blob.core.windows.net/mycontainer/myblockblob?sv=2019-12-12&ss=b&srt=coxxxxx
Next, in the postman, paste the new url. And in the Headers, you can remove Authorization field.
The test result is as below:
#sathishKumar
If you look closely in this article Authorize with Shared Key
The syntax is as below :
Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"
It is the signature that is passed along and not the Account key.
Signature is a Hash-based Message Authentication Code (HMAC) constructed from the request and computed by using the SHA256 algorithm, and then encoded by using Base64 encoding.
There are detailed steps how to construct the same mentioned on the above document.
Also, came across the post which talks about a PowerShell script which creates an Signature string through the Powershell that could be useful for you.
Sample Powershell Script
C# Implementation :
internal static AuthenticationHeaderValue GetAuthorizationHeader(
string storageAccountName, string storageAccountKey, DateTime now,
HttpRequestMessage httpRequestMessage, string ifMatch = "", string md5 = "")
{
// This is the raw representation of the message signature.
HttpMethod method = httpRequestMessage.Method;
String MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}",
method.ToString(),
(method == HttpMethod.Get || method == HttpMethod.Head) ? String.Empty
: httpRequestMessage.Content.Headers.ContentLength.ToString(),
ifMatch,
GetCanonicalizedHeaders(httpRequestMessage),
GetCanonicalizedResource(httpRequestMessage.RequestUri, storageAccountName),
md5);
// Now turn it into a byte array.
byte[] SignatureBytes = Encoding.UTF8.GetBytes(MessageSignature);
// Create the HMACSHA256 version of the storage key.
HMACSHA256 SHA256 = new HMACSHA256(Convert.FromBase64String(storageAccountKey));
// Compute the hash of the SignatureBytes and convert it to a base64 string.
string signature = Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));
// This is the actual header that will be added to the list of request headers.
AuthenticationHeaderValue authHV = new AuthenticationHeaderValue("SharedKey",
storageAccountName + ":" + signature);
return authHV;
}
I pieced together code to authorize to Microsoft Azure for a Storage Account Table operation.
function TAzureStorageAPI.GetAuthHeader(RequestMethod,Ressource,Time:UTF8String): String;
Var
KeyBytes:TBytes;
DataBytes:TBytes;
TimeString,
StringtoSign:UTF8String;
begin
StringtoSign:=Uppercase(RequestMethod)+LF+ //RequestMethod
''+LF+ //contentMD5
'application/json; charset=ISO-8859-1'+LF+ //contentType
TimeString+LF+ //requestDate
Ressource; //Ressource
keyBytes:=TNetEncoding.Base64.DecodeStringToBytes(FAccessKey);
dataBytes:=TEncoding.UTF8.GetBytes(StringToSign);
result:= (TNetEncoding.Base64.EncodeBytesToString(THashSHA2.GetHMACAsBytes(dataBytes, keyBytes)));
end;
function TAzureStorageAPI.Insert(PartitionKey,RowKey:String; Data:tlkJSONObject):tlkJSONObject;
Var
PostHeaders:TStringlist;
Time:TDateTime;
TimeString:String;
begin
Socket.Request.Accept:='application/json;odata=minimalmetadata';
PostHeaders:=TStringlist.Create;
PostHeaders.Add('x-ms-version:2019-07-07');
Time:=TTimeZone.Local.ToUniversalTime(Now);
TimeString:=FormatDateTime('ddd, dd mmm yyyy hh:nn:ss',Time)+' UTC';
PostHeaders.Add('Date:'+TimeString);
PostHeaders.Add('MaxDataServiceVersion:3.0;NetFx');
PostHeaders.Add('DataServiceVersion:3.0;NetFx');
PostHeaders.Add('Authorization:SharedKey '+FStorageAccount+':'+GetAuthHeader('post','/'+FStorageAccount+'/'+FTable,TimeString));
Host:=FStorageAccount+'.table.core.windows.net';
Data.Add('PartitionKey',PartitionKey);
Data.add('RowKey',RowKey);
result:=WebPostData('/'+FTable,PostHeaders,Data)as tlkJSONObject;
PostHeaders.Free;
end;
StorageAccount is the name I get from the SharedKey-Options
Table is the name of the ressource from the Table Service
and AccessKey is Key1 from the SharedKey-Options
The StringToSign is
'POST'#$A#$A'application/json; charset=ISO-8859-1'#$A'Mo, 27 Apr 2020 18:02:33 UTC'#$A'/smartflatlog/Log'
I am not using any access policy on the resource.
After fixing a bug in the passing of the headers I see the following transmit on the wire:
Ges 27.04.2020 20:02:48: POST /Log HTTP/1.1<EOL>Content-Type: application/json; charset=ISO-8859-1<EOL>Content-Length: 104<EOL>x-ms-version: 2019-07-07<EOL>Date: Mo, 27 Apr 2020 18:02:33 UTC<EOL>MaxDataServiceVersion: 3.0;NetFx<EOL>DataServiceVersion: 3.0;NetFx<EOL>Authorization: SharedKey smartflatlog:KVtJ*********************************A5zOME=<EOL>Host: smartflatlog.table.core.windows.net<EOL>Accept: application/json;odata=minimalmetadata<EOL>User-Agent: Demo<EOL><EOL>
Ges 27.04.2020 20:02:48: {"Level":"Debug","LogText":"something to note","Application":"Demo","PartitionKey":"Demo","RowKey":"13"}
Erh 27.04.2020 20:02:48: HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.<EOL>Content-Length: 299<EOL>Content-Type: application/json<EOL>Server: Microsoft-HTTPAPI/2.0<EOL>x-ms-request-id: 86f7fd8d-2002-0021-63be-1c5d47000000<EOL>x-ms-error-code: AuthenticationFailed<EOL>Date: Mon, 27 Apr 2020 18:02:49 GMT<EOL><EOL>{"odata.error":{"code":"AuthenticationFailed","message":{"lang":"en-US","value":"Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:86f7fd8d-2002-0021-63be-1c5d47000000\nTime:2020-04-27T18:02:49.6860540Z"}}}
If you want to insert an entity with Sharekey auth, the stringtosign should be like
StringToSign = VERB + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
CanonicalizedResource;
For example
RequestMethod:= 'GET';
dateInRfc1123Format:= TTimeZone.Local.ToUniversalTime(Now);
TheDate:= formatdatetime('ddd, dd mmm yyyy hh:nn:ss "GMT"',dateInRfc1123Format);
contentType:='application/json;odata=nometadata'
canonicalizedResource:= "/yourAccount/yourTable"
stringToSign:= format('%s\n'+ // request method
'\n' + // content md5
'%s\n' + // content type
'%s\n' + // date
'%s', // canonicalized ResourceL,
[RequestMethod,
contentType,
TheDate,
canonicalizedResource]);
For more details, please refer to the document and the document
Using Apache Http 4.5 MultipartEntityBuilder and can't seem to figure out why the StringBody(String, ContentType) constructor doesn't actually output the Content-Type in the request form body.
public HttpRequestBase build() throws UnsupportedEncodingException {
HttpPost httpPost = new HttpPost("https://{server}/restapi/{apiVersion}/accounts/{accountId}/envelopes");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setContentType(ContentType.MULTIPART_FORM_DATA);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setBoundary("AAA");
//add form body
builder.addPart(generateJsonFormBodyPart());
//add file body
builder.addPart(generateFileFormBodyPart()); //<--intentionally omitted
HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
return httpPost;
}
private FormBodyPart generateJsonFormBodyPart() throws UnsupportedEncodingException{
StringBody json = new StringBody(packageJson(), ContentType.APPLICATION_JSON); //<--THIS DOESN'T SEEM TO WORK
StringBuilder buffer = new StringBuilder();
buffer.append("form-data");
String contentDisposition = buffer.toString();
FormBodyPartBuilder partBuilder = FormBodyPartBuilder.create("application/json", json);
partBuilder.setField(MIME.CONTENT_DISPOSITION, contentDisposition);
FormBodyPart fbp = partBuilder.build();
return fbp;
}
The file portion outputs ok but I get a "Bad Request" return from the peer which I assume is because it has very specific request parameters.
Required Request Output
Accept: application/json
Content-Type: multipart/form-data; boundary=AAA
--AAA
Content-Type: application/json
Content-Disposition: form-data
json removed
--AAA
Content-Type: application/pdf
Content-Disposition: file; filename="test1.pdf";documentid=1
document removed
Actual Apache Http 4.5 Output
X-Docusign-Act-As-User: xyz#company.com
Accept-Encoding: gzip,deflate
User-Agent: Apache-HttpClient/4.5 (Java/1.8.0_65)
Connect-Time: 0
Host: requestb.in
Connection: close
Content-Length: 3178
Authorization: bearer xxxxrandomoauthtokenxxxxx
Content-Type: multipart/form-data; boundary=AAA; charset=ISO-8859-1
Via: 1.1 vegur
X-Request-Id: 89cd1cf5-3615-41e8-84ba-cd076a03af67
Total-Route-Time: 0
--AAA
Content-Disposition: form-data //<--the problem. should be application/json no?
{"status":"created","emailBlurb":"Welcome to Confluence","emailSubject":"Welcome to Confluence","documents":{"name":"Welcome to Confluence.html","documentId":"1","order":"1"},"recipients":{}}
--AAA
Content-Disposition: file; filename="Welcome.html";documentid=1
Content-Type: text/html; charset=ISO-8859-1
h t m l string removed
Q: So why does the ContentType in the StringBody constructor get ignored? Is there a workaround or am I doing it wrong?
I'm convinced this is a bug in Apache Http Mime for the FormBodyPart.build() when processing a StringBody. Evidence from release notes of 4.5.2 indicating bug fixes for other "body" types not outputting "Content-Type" supports this. I will be logging a defect.
The Workaround:
private FormBodyPart generateJsonFormBodyPart() throws UnsupportedEncodingException{
StringBody json = new StringBody(getMyJsonStuff(), ContentType.APPLICATION_JSON); //<--THE GOGGLES, THEY DO NOTHING!!
StringBuilder buffer = new StringBuilder();
buffer.append("form-data");
buffer.append("\r\n");
buffer.append("Content-Type: application/json"); //<--tack this on to the
String kludgeForDispositionAndContentType = buffer.toString();
FormBodyPartBuilder partBuilder = FormBodyPartBuilder.create("stuff", json);
partBuilder.setField(MIME.CONTENT_DISPOSITION, kludgeForDispositionAndContentType);
FormBodyPart fbp = partBuilder.build();
return fbp;
}
Works like a charm.
I am having a problem with stringtosign authentication for azure table pagination query.
This is the current stringtosign im using :
GET\n\n\nFri, 05 Sep 2014 03:57:11 GMT\n/mystorageaccount/mytablename\nNextPartitionKey:1!20!UmFjZSBNZW1iZXJfNA--\nNextRowKey:1!12!TmFtZV85ODE-
Is there anything wrong with this stringtosign authentication?
The rest of the Headers are exactly the same as Fiddle.
Example
GET /mytablename?NextPartitionKey=1%2120%21UmFjZSBNZW1iZXJfNA--&NextRowKey=1%2112%21TmFtZV85ODE- HTTP/1.1
Host: mystorageaccount.table.core.windows.net
x-ms-version: 2014-02-14
x-ms-date: Fri, 05 Sep 2014 05:49:19 GMT
Authorization: SharedKey mystorageaccount:GD2w4pqsllzIOixNF/AfFqLkZhYzLpjK67a8OI7j6Go=
Accept: application/atom+xml
Accept-Charset: UTF-8
DataServiceVersion: 3.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
I have read through both
http://msdn.microsoft.com/library/azure/dd179428.aspx
http://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
Hi Gaurav Mantri,
It still did not work. I'll paste the request, my stringtosign and the response below:
GET /mytablename?NextPartitionKey=1%2120%21UmFjZSBNZW1iZXJfNA--&NextRowKey=1%2112%21TmFtZV85ODE- HTTP/1.1
Host: mystorageaccount.table.core.windows.net
x-ms-version: 2014-02-14
x-ms-date: Fri, 05 Sep 2014 07:05:12 GMT
Authorization: SharedKey mystorageaccount:HSYfO1Baadqcd4bQO5Q6uN1hrr2aXtLcQbFPkWgIXuw=
Accept: application/atom+xml
Accept-Charset: UTF-8
DataServiceVersion: 3.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
String to sign:
GET\n\n\nFri, 05 Sep 2014 07:05:12 GMT\n/mystorageaccount/mytablename\nnextpartitionkey:1!20!UmFjZSBNZW1iZXJfNA--\nnextrowkey:1!12!TmFtZV85ODE-
Response:
<?xml version=\"1.0\" encoding=\"utf-8\"?><m:error xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"><m:code>AuthenticationFailed</m:code><m:message xml:lang=\"en-US\">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:37272f11-0002-0014-5aa7-f7dd1c000000
Time:2014-09-05T07:05:09.5720897Z</m:message></m:error>
I had an opportunity to actually write the code and try it out. Basically when creating CanonicalizedResource string for table resources, you need not include the query string parameters other than comp querystring parameter. Essentially this is what you would need to follow from the documentation (http://msdn.microsoft.com/library/azure/dd179428.aspx):
2009-09-19 Shared Key Lite and Table Service Format
This format supports Shared Key and Shared Key Lite for all versions
of the Table service, and Shared Key Lite for the 2009-09-19 version
of the Blob and Queue services and 2014-02-14 of the File service.
This format is identical to that used with previous versions of the
storage services. Construct the CanonicalizedResource string in this
format as follows:
Beginning with an empty string (""), append a forward slash (/), followed by the name of the account that owns the resource being
accessed.
Append the resource's encoded URI path. If the request URI addresses a component of the resource, append the appropriate query
string. The query string should include the question mark and the comp
parameter (for example, ?comp=metadata). No other parameters should be
included on the query string.
Once you do that, your code should run just fine. Here's the sample code I wrote:
static void QueryTable()
{
var requestMethod = "GET";
var storageServiceVersion = "2014-02-14";
var date = DateTime.UtcNow.ToString("R");
var canonicalizedResource = string.Format("/{0}/{1}", StorageAccount, TableName);
var stringToSign = string.Format("{0}\n\n\n{1}\n{2}", requestMethod, date, canonicalizedResource);
var authorizationHeader = GetAuthorizationHeader(stringToSign);
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(TableEndpoint);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("x-ms-date", date);
httpClient.DefaultRequestHeaders.Add("x-ms-version", storageServiceVersion);
httpClient.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
var result = httpClient.GetAsync(TableName + "?NextPartitionKey=1!48!VXwzMzg0MDAzOWYzMjQ0ZDgxOWZjZmM5M2EyMzNkM2IxOA--&NextRowKey=1!0!");
result.Wait();
}
}
static string GetAuthorizationHeader(string canonicalizedString)
{
var signature = string.Empty;
using (var hash = new HMACSHA256(Convert.FromBase64String(StorageAccountKey)))
{
var data = Encoding.UTF8.GetBytes(canonicalizedString);
signature = Convert.ToBase64String(hash.ComputeHash(data));
}
return string.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKey", StorageAccount, signature);
}
Based on the documentation here: http://msdn.microsoft.com/library/azure/dd179428.aspx (2009-09-19 Shared Key Format Section, point #4), you would need to convert all query parameters to lowercase. So your canonicalized resource string should be:
GET\n\n\nFri, 05 Sep 2014 03:57:11 GMT\n/mystorageaccount/mytablename\nnextpartitionkey:1!20!UmFjZSBNZW1iZXJfNA--\nnextrowkey:1!12!TmFtZV85ODE-
Give it a try. That should take care of the problem.