Start work on a test program to communicate with Windows Azure.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Mon, 3 May 2010 02:10:02 +0000 (19:10 -0700)
committerMichael Vrable <mvrable@cs.ucsd.edu>
Mon, 3 May 2010 02:10:02 +0000 (19:10 -0700)
This could provide another backend for BlueSky, and it might be nice to at
least make a few benchmark measurements of Azure to complement the S3 ones.

azure/azure.py [new file with mode: 0755]

diff --git a/azure/azure.py b/azure/azure.py
new file mode 100755 (executable)
index 0000000..dd0173e
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/python2.6
+
+"""A simple test API for accessing Windows Azure blob storage.
+
+Parts of the code are modeled after boto (a library for accessing Amazon Web
+Services), but this code is far less general and is meant only as a
+proof-of-concept."""
+
+import base64, hashlib, hmac, httplib, os, time
+
+# The version of the Azure API we implement; sent in the x-ms-version header.
+API_VERSION = '2009-09-19'
+
+def uri_decode(s):
+    # TODO
+    return s
+
+def add_auth_header(headers, method, path):
+    header_order = ['Content-Encoding', 'Content-Language', 'Content-Length',
+                    'Content-MD5', 'Content-Type', 'Date', 'If-Modified-Since',
+                    'If-Match', 'If-None-Match', 'If-Unmodified-Since',
+                    'Range']
+
+    if not headers.has_key('Date'):
+        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+                                        time.gmtime())
+    if not headers.has_key('x-ms-version'):
+        headers['x-ms-version'] = API_VERSION
+
+    StringToSign = method + "\n"
+    for h in header_order:
+        if h in headers:
+            StringToSign += headers[h] + "\n"
+        else:
+            StringToSign += "\n"
+
+    # Add Canonicalized Headers
+    canonized = []
+    for (k, v) in headers.items():
+        k = k.lower()
+        if k.startswith('x-ms-'):
+            canonized.append((k, v))
+    canonized.sort()
+    for (k, v) in canonized:
+        StringToSign += "%s:%s\n" % (k, v)
+
+    # Add CanonicalizedHeaders Resource
+    account_name = os.environ['AZURE_ACCOUNT_NAME']
+    account_name = 'bluesky'
+    resource = "/" + account_name
+    if '?' not in path:
+        resource += path
+    else:
+        (path, params) = path.split('?', 1)
+        params = [p.split('=') for p in params.split("&")]
+        params = dict((k.lower(), uri_decode(v)) for (k, v) in params)
+        resource += path
+        for k in sorted(params):
+            resource += "\n%s:%s" % (k, params[k])
+    StringToSign += resource
+
+    # print "String to sign:", repr(StringToSign)
+
+    secret_key = os.environ['AZURE_SECRET_KEY']
+    secret_key = base64.b64decode(secret_key)
+    h = hmac.new(secret_key, digestmod=hashlib.sha256)
+    h.update(StringToSign)
+
+    signature = base64.b64encode(h.digest())
+    headers['Authorization'] = "SharedKey %s:%s" % (account_name, signature)
+
+def generate_request(path, method='GET', body="", headers={}):
+    host = os.environ['AZURE_ACCOUNT_NAME'] + ".blob.core.windows.net"
+    #auth_path = conn.calling_format.build_auth_path(self.bucket, key)
+
+    headers = headers.copy()
+    headers['Content-Length'] = str(len(body))
+    if len(body) > 0:
+        headers['Content-MD5'] = base64.b64encode(hashlib.md5(body).digest())
+    add_auth_header(headers, method, path)
+
+    req = "%s %s HTTP/1.1\r\nHost: %s\r\n" % (method, path, host)
+    req = req + ''.join("%s: %s\r\n" % h for h in headers.items()) + "\r\n"
+    print req
+
+    conn = httplib.HTTPConnection(host)
+    conn.request(method, path, body, headers)
+    response = conn.getresponse()
+    print "Response:", response.status
+    print "Headers:", response.getheaders()
+    print "Body:", response.read()
+
+if False:
+    generate_request("/?comp=list")