using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Globalization;
using System.Configuration;
namespace Windows.Azure.Storage.Blobs
{
public enum BlobConnectionModes
{
Local,
Cloud
}
public class ConnectionDetails
{
public string Account { get; set; }
public string Key { get; set; }
public string Endpoint { get; set; }
public BlobConnectionModes ConnectionMode {get; set;}
public ConnectionDetails(BlobConnectionModes connectionMode)
{
ConnectionMode = connectionMode;
}
public string GetConnectionURI()
{
return String.Format(Endpoint, Account);
}
}
///
/// Blob Request is based on the HttpWebRequest. It provides extra capabilities to handle the REST API for Blob Storage
///
public class BlobRequest
{
private ConnectionDetails connectionDetails;
public BlobRequest(ConnectionDetails connectionDetails)
{
this.connectionDetails = connectionDetails;
}
public BlobResponse SetContainerPermissions(string containerName, bool makePublic)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(containerName, new string [] {"acl"}));
request.Method = "PUT";
request.ContentLength = 0;
SetDateHeader(request);
SetContainerACL(request, makePublic);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
public BlobResponse CreateContainer(string containerName)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(containerName, null));
request.Method = "PUT";
request.ContentLength = 0;
SetDateHeader(request);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
public BlobResponse RemoveContainer(string containerName)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(containerName, null));
request.Method = "DELETE";
request.ContentLength = 0;
SetDateHeader(request);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
public BlobResponse GetContainers()
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(String.Empty, new string [] {"list"}));
request.Method = "GET";
request.ContentLength = 0;
SetDateHeader(request);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
public BlobResponse UploadBlob(string containerName, string blobName, byte[] blobContents)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(String.Format("{0}/{1}", containerName, blobName), null));
request.Method = "PUT";
request.ContentLength = blobContents.Length;
SetDateHeader(request);
SetAuthorizationHeader(request);
SetBodyContent(request,blobContents);
return DispatchRequest(request);
}
public BlobResponse SetBlobMetadata(string containerName, string blobName, Dictionary metadata)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(String.Format("{0}/{1}", containerName, blobName), new string [] {"metadata"}));
request.Method = "PUT";
request.ContentLength = 0;
SetDateHeader(request);
SetMetadataHeaders(request, metadata);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
public BlobResponse GetBlobMetadata(string containerName, string blobName)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(String.Format("{0}/{1}", containerName, blobName), new string[] { "metadata" }));
request.Method = "HEAD";
request.ContentLength = 0;
SetDateHeader(request);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
private void SetMetadataHeaders(HttpWebRequest request, Dictionary metadata)
{
foreach (KeyValuePair metadataItem in metadata)
{
request.Headers.Add(String.Format("x-ms-meta-{0}:{1}", metadataItem.Key, metadataItem.Value));
}
}
public BlobResponse GetBlobs(string containerName)
{
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(GetUri(String.Format("{0}", containerName), new string [] {"list"}));
request.Method = "GET";
request.ContentLength = 0;
SetDateHeader(request);
SetAuthorizationHeader(request);
return DispatchRequest(request);
}
private void SetContainerACL(HttpWebRequest request, bool makePublic)
{
request.Headers.Add("x-ms-prop-publicaccess", makePublic.ToString().ToLower());
}
private void SetBodyContent(HttpWebRequest request, byte[] blobContents)
{
request.GetRequestStream().Write(blobContents, 0, blobContents.Length);
}
private string GetUri(string function, string [] comps)
{
StringBuilder uri = new StringBuilder();
uri.Append(this.connectionDetails.GetConnectionURI());
uri.AppendFormat("{0}", function);
if (comps != null && comps.Length > 0)
{
uri.Append("?");
for(int i = 0; i < comps.Length; i++)
{
uri.AppendFormat("comp={0}", comps[i]);
if (i < comps.Length - 1)
{
uri.Append("&");
}
}
}
return uri.ToString();
}
private BlobResponse DispatchRequest(HttpWebRequest request)
{
try
{
using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
return new BlobResponse(response);
}
}
catch (WebException ex)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response)
{
return new BlobResponse(errorResponse);
}
}
}
private void SetAuthorizationHeader(HttpWebRequest request)
{
// Now sign the request
// For a blob, you need to use this Canonical form:
// VERB + "\n" +
// Content - MD5 + "\n" +
// Content - Type + "\n" +
// Date + "\n" +
// CanonicalizedHeaders +
// CanonicalizedResource;
StringBuilder signature = new StringBuilder();
// Verb
signature.Append(String.Format("{0}{1}", request.Method,"\n"));
// Content-MD5 Header
signature.Append("\n");
// Content-Type Header
signature.Append("\n");
// Then Date, if we have already added the x-ms-date header, leave this null
signature.Append("\n");
// Now for CanonicalizedHeaders
// TODO: Replace with LINQ statement
foreach (string header in request.Headers)
{
if (header.StartsWith("x-ms"))
{
signature.Append(String.Format("{0}:{1}\n", header, request.Headers[header]));
}
}
// Now for CanonicalizedResource
// Format is /{0}/{1} where 0 is name of the account and 1 is resources URI path
signature.Append(String.Format("/{0}{1}", connectionDetails.Account, request.RequestUri.PathAndQuery));
// Next, we need to encode our signature using the HMAC-SHA256 algorithm
byte[] signatureByteForm = System.Text.Encoding.UTF8.GetBytes(signature.ToString());
System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(connectionDetails.Key));
// Now build the Authorization header
String authHeader = String.Format(CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
connectionDetails.Account,
System.Convert.ToBase64String(hasher.ComputeHash(signatureByteForm)
));
// And add the Authorization header to the request
request.Headers.Add("Authorization", authHeader);
}
private void SetDateHeader(HttpWebRequest request)
{
request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}
}
}