• R/O
  • HTTP
  • SSH
  • HTTPS

提交

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

winazurestorageのフォーク


Commit MetaInfo

修訂1c45a2a66d33081ae5f4d1ca1a8456e24db51c0f (tree)
時間2012-01-10 08:08:54
作者Steve Marx <Steve.Marx@micr...>
CommiterSteve Marx

Log Message

support latest x-ms-version, add put_block, and add a few more tests

Change Summary

差異

--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
1+*.pyc
\ No newline at end of file
--- a/test.py
+++ b/test.py
@@ -1,6 +1,8 @@
11 from winazurestorage import *
2+import base64
3+import sys
24
3-def do_blob_tests():
5+def do_blob_tests(account, key):
46 '''Expected output:
57 Starting blob tests
68 create_container: 201
@@ -10,15 +12,41 @@ def do_blob_tests():
1012 Done.
1113 '''
1214 print "Starting blob tests"
13- blobs = BlobStorage()
15+ if account is None or key is None: blobs = BlobStorage()
16+ else: blobs = BlobStorage(CLOUD_BLOB_HOST, account, key)
1417 print "\tcreate_container: %d" % blobs.create_container("testcontainer", True)
15- print "\tput_blob: %d" % blobs.put_blob("testcontainer", "testblob.txt", "Hello, World!", "text/plain")
18+ print "\tput_blob: %d" % blobs.put_blob("testcontainer", "testblob.txt", "Hello, World!")
1619 print "\tget_blob: %s" % blobs.get_blob("testcontainer", "testblob.txt")
20+ print "\tput_block: %d" % blobs.put_block("testcontainer", "testblob.txt", base64.encodestring('foobar'), 'something')
1721 print "\tdelete_container: %d" % blobs.delete_container("testcontainer")
1822 print "Done."
1923
20-def run_tests():
21- do_blob_tests()
24+def do_table_tests(account, key):
25+ if account is None or key is None:
26+ print "Skipping table tests, since no account and key were passed on the command line."
27+ return
28+ print "Starting table tests"
29+ tables = TableStorage(CLOUD_TABLE_HOST, account, key)
30+ print "\tcreate_table: %d" % tables.create_table("testtable")
31+ print "\tget_all: %d" % len(tables.get_all("testtable"))
32+ print "\tdelete_table: %d" % tables.delete_table("testtable")
33+ print "Done"
34+
35+def do_queue_tests(account, key):
36+ print "Starting queue tests"
37+ if account is None or key is None: queues = QueueStorage()
38+ else: queues = QueueStorage(CLOUD_QUEUE_HOST, account, key)
39+ print "\tcreate_queue: %d" % queues.create_queue("testqueue")
40+ print "\tdelete_queue: %d" % queues.delete_queue("testqueue")
41+ print "Done"
42+
43+def run_tests(account, key):
44+ do_blob_tests(account, key)
45+ do_table_tests(account, key)
46+ do_queue_tests(account, key)
2247
2348 if __name__ == '__main__':
24- run_tests()
\ No newline at end of file
49+ if len(sys.argv) > 2:
50+ run_tests(sys.argv[1], sys.argv[2])
51+ else:
52+ run_tests(None, None)
\ No newline at end of file
--- a/winazurestorage.py
+++ b/winazurestorage.py
@@ -16,17 +16,19 @@ from xml.dom import minidom #TODO: Use a faster way of processing XML
1616 import re
1717 from urllib2 import Request, urlopen, URLError
1818 from urllib import urlencode
19-from urlparse import urlsplit
19+from urlparse import urlsplit, parse_qs
2020 from datetime import datetime, timedelta
2121
2222 DEVSTORE_ACCOUNT = "devstoreaccount1"
2323 DEVSTORE_SECRET_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
2424
2525 DEVSTORE_BLOB_HOST = "127.0.0.1:10000"
26+DEVSTORE_QUEUE_HOST = "127.0.0.1:10001"
2627 DEVSTORE_TABLE_HOST = "127.0.0.1:10002"
2728
2829 CLOUD_BLOB_HOST = "blob.core.windows.net"
2930 CLOUD_TABLE_HOST = "table.core.windows.net"
31+CLOUD_QUEUE_HOST = "queue.core.windows.net"
3032
3133 PREFIX_PROPERTIES = "x-ms-prop-"
3234 PREFIX_METADATA = "x-ms-meta-"
@@ -64,27 +66,39 @@ class SharedKeyCredentials(object):
6466 path = path[path.index('/'):]
6567
6668 canonicalized_resource = "/" + self._account + path
67- match = re.search(r'comp=[^&]*', query)
68- if match is not None:
69- canonicalized_resource += "?" + match.group(0)
70-
69+ q = parse_qs(query)
70+ if len(q.keys()) > 0:
71+ canonicalized_resource +=''.join(["\n%s:%s" % (k, ','.join(sorted(q[k]))) for k in sorted(q.keys())])
72+
7173 if use_path_style_uris is None:
7274 use_path_style_uris = re.match('^[\d.:]+$', host) is not None
7375
76+ request.add_header(PREFIX_STORAGE_HEADER + 'version', '2011-08-18')
7477 request.add_header(PREFIX_STORAGE_HEADER + 'date', time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())) #RFC 1123
78+ if for_tables:
79+ request.add_header('Date', request.get_header((PREFIX_STORAGE_HEADER + 'date').capitalize()))
80+ request.add_header('DataServiceVersion', '1.0;NetFx')
81+ request.add_header('MaxDataServiceVersion', '1.0;NetFx')
7582 canonicalized_headers = NEW_LINE.join(('%s:%s' % (k.lower(), request.get_header(k).strip()) for k in sorted(request.headers.keys(), lambda x,y: cmp(x.lower(), y.lower())) if k.lower().startswith(PREFIX_STORAGE_HEADER)))
7683
7784 string_to_sign = request.get_method().upper() + NEW_LINE # verb
78- string_to_sign += NEW_LINE # MD5 not required
79- if request.get_header('Content-type') is not None: # Content-Type
80- string_to_sign += request.get_header('Content-type')
81- string_to_sign += NEW_LINE
82- if for_tables: string_to_sign += request.get_header(PREFIX_STORAGE_HEADER.capitalize() + 'date') + NEW_LINE
83- else: string_to_sign += NEW_LINE # Date
8485 if not for_tables:
85- string_to_sign += canonicalized_headers + NEW_LINE # Canonicalized headers
86- string_to_sign += canonicalized_resource # Canonicalized resource
87-
86+ string_to_sign += (request.get_header('Content-encoding') or '') + NEW_LINE
87+ string_to_sign += (request.get_header('Content-language') or '') + NEW_LINE
88+ string_to_sign += (request.get_header('Content-length') or '') + NEW_LINE
89+ string_to_sign += (request.get_header('Content-md5') or '') + NEW_LINE
90+ string_to_sign += (request.get_header('Content-type') or '') + NEW_LINE
91+ string_to_sign += (request.get_header('Date') or '') + NEW_LINE
92+ if not for_tables:
93+ string_to_sign += (request.get_header('If-modified-since') or '') + NEW_LINE
94+ string_to_sign += (request.get_header('If-match') or '') + NEW_LINE
95+ string_to_sign += (request.get_header('If-none-match') or '') + NEW_LINE
96+ string_to_sign += (request.get_header('If-unmodified-since') or '') + NEW_LINE
97+ string_to_sign += (request.get_header('Range') or '') + NEW_LINE
98+ if not for_tables:
99+ string_to_sign += canonicalized_headers + NEW_LINE
100+ string_to_sign += canonicalized_resource
101+
88102 request.add_header('Authorization', 'SharedKey ' + self._account + ':' + base64.encodestring(hmac.new(self._key, unicode(string_to_sign).encode("utf-8"), hashlib.sha256).digest()).strip())
89103 return request
90104
@@ -130,7 +144,7 @@ class TableEntity(object): pass
130144 class QueueMessage(): pass
131145
132146 class QueueStorage(Storage):
133- def __init__(self, host, account_name, secret_key, use_path_style_uris = None):
147+ def __init__(self, host = DEVSTORE_QUEUE_HOST, account_name = DEVSTORE_ACCOUNT, secret_key = DEVSTORE_SECRET_KEY, use_path_style_uris = None):
134148 super(QueueStorage, self).__init__(host, account_name, secret_key, use_path_style_uris)
135149
136150 def create_queue(self, name):
@@ -279,7 +293,7 @@ class BlobStorage(Storage):
279293 super(BlobStorage, self).__init__(host, account_name, secret_key, use_path_style_uris)
280294
281295 def create_container(self, container_name, is_public = False):
282- req = RequestWithMethod("PUT", "%s/%s" % (self.get_base_url(), container_name))
296+ req = RequestWithMethod("PUT", "%s/%s?restype=container" % (self.get_base_url(), container_name))
283297 req.add_header("Content-Length", "0")
284298 if is_public: req.add_header(PREFIX_PROPERTIES + "publicaccess", "true")
285299 self._credentials.sign_request(req)
@@ -290,7 +304,7 @@ class BlobStorage(Storage):
290304 return e.code
291305
292306 def delete_container(self, container_name):
293- req = RequestWithMethod("DELETE", "%s/%s" % (self.get_base_url(), container_name))
307+ req = RequestWithMethod("DELETE", "%s/%s?restype=container" % (self.get_base_url(), container_name))
294308 self._credentials.sign_request(req)
295309 try:
296310 response = urlopen(req)
@@ -314,6 +328,7 @@ class BlobStorage(Storage):
314328 def put_blob(self, container_name, blob_name, data, content_type = "", metadata = {}):
315329 req = RequestWithMethod("PUT", "%s/%s/%s" % (self.get_base_url(), container_name, blob_name), data=data)
316330 req.add_header("Content-Length", "%d" % len(data))
331+ req.add_header('x-ms-blob-type', 'BlockBlob')
317332 for key, value in metadata.items():
318333 req.add_header("x-ms-meta-%s" % key, value)
319334 req.add_header("Content-Type", content_type)
@@ -372,6 +387,18 @@ class BlobStorage(Storage):
372387 except: marker = None
373388 if marker is None: break
374389
390+ def put_block(self, container_name, blob_name, block_id, data):
391+ encoded_block_id = urlencode({"comp": "block", "blockid": block_id})
392+ req = RequestWithMethod("PUT", "%s/%s/%s?%s" % (self.get_base_url(), container_name, blob_name, encoded_block_id), data=data)
393+ req.add_header("Content-Type", "")
394+ req.add_header("Content-Length", "%d" % len(data))
395+ self._credentials.sign_request(req)
396+ try:
397+ response = urlopen(req)
398+ return response.code
399+ except URLError, e:
400+ return e.code
401+
375402 def main():
376403 pass
377404