+ throw new ArgumentNullException("objectName");
+ if (hash==null)
+ throw new ArgumentNullException("hash");
+ if (String.IsNullOrWhiteSpace(Token))
+ throw new InvalidOperationException("Invalid Token");
+ if (StorageUrl == null)
+ throw new InvalidOperationException("Invalid Storage Url");
+ Contract.EndContractBlock();
+
+
+ //Don't use a timeout because putting the hashmap may be a long process
+ var client = new RestClient(_baseClient) { Timeout = 0 };
+ if (!String.IsNullOrWhiteSpace(account))
+ client.BaseAddress = GetAccountUrl(account);
+
+ //The container and objectName are relative names. They are joined with the client's
+ //BaseAddress to create the object's absolute address
+ var builder = client.GetAddressBuilder(container, objectName);
+ builder.Query = "format=json&hashmap";
+ var uri = builder.Uri;
+
+
+ //Send the tree hash as Json to the server
+ client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
+ var uploadTask=client.UploadStringTask(uri, "PUT", hash.ToJson());
+
+
+ return uploadTask.ContinueWith(t =>
+ {
+
+ var empty = (IList<string>)new List<string>();
+
+
+ //The server will respond either with 201-created if all blocks were already on the server
+ if (client.StatusCode == HttpStatusCode.Created)
+ {
+ //in which case we return an empty hash list
+ return empty;
+ }
+ //or with a 409-conflict and return the list of missing parts
+ //A 409 will cause an exception so we need to check t.IsFaulted to avoid propagating the exception
+ if (t.IsFaulted)
+ {
+ var ex = t.Exception.InnerException;
+ var we = ex as WebException;
+ var response = we.Response as HttpWebResponse;
+ if (response!=null && response.StatusCode==HttpStatusCode.Conflict)
+ {
+ //In case of 409 the missing parts will be in the response content
+ using (var stream = response.GetResponseStream())
+ using(var reader=new StreamReader(stream))
+ {
+ //We need to cleanup the content before returning it because it contains
+ //error content after the list of hashes
+ var hashes = new List<string>();
+ string line=null;
+ //All lines up to the first empty line are hashes
+ while(!String.IsNullOrWhiteSpace(line=reader.ReadLine()))
+ {
+ hashes.Add(line);
+ }
+
+ return hashes;
+ }
+ }
+ else
+ //Any other status code is unexpected and the exception should be rethrown
+ throw ex;
+
+ }
+ //Any other status code is unexpected but there was no exception. We can probably continue processing
+ else
+ {
+ Log.WarnFormat("Unexcpected status code when putting map: {0} - {1}",client.StatusCode,client.StatusDescription);
+ }
+ return empty;
+ });
+
+ }
+
+ public Task<byte[]> GetBlock(string account, string container, Uri relativeUrl, long start, long? end)
+ {
+ if (String.IsNullOrWhiteSpace(Token))
+ throw new InvalidOperationException("Invalid Token");
+ if (StorageUrl == null)
+ throw new InvalidOperationException("Invalid Storage Url");
+ if (String.IsNullOrWhiteSpace(container))
+ throw new ArgumentNullException("container");
+ if (relativeUrl== null)
+ throw new ArgumentNullException("relativeUrl");
+ if (end.HasValue && end<0)
+ throw new ArgumentOutOfRangeException("end");
+ if (start<0)
+ throw new ArgumentOutOfRangeException("start");
+ Contract.EndContractBlock();
+
+
+ //Don't use a timeout because putting the hashmap may be a long process
+ var client = new RestClient(_baseClient) {Timeout = 0, RangeFrom = start, RangeTo = end};
+ if (!String.IsNullOrWhiteSpace(account))
+ client.BaseAddress = GetAccountUrl(account);
+
+ var builder = client.GetAddressBuilder(container, relativeUrl.ToString());
+ var uri = builder.Uri;
+
+ return client.DownloadDataTask(uri)
+ .ContinueWith(t=>
+ {
+ client.Dispose();
+ return t.Result;
+ });
+ }
+
+
+ public Task PostBlock(string account, string container, byte[] block, int offset, int count)
+ {
+ if (String.IsNullOrWhiteSpace(container))
+ throw new ArgumentNullException("container");
+ if (block == null)
+ throw new ArgumentNullException("block");
+ if (offset < 0 || offset >= block.Length)
+ throw new ArgumentOutOfRangeException("offset");
+ if (count < 0 || count > block.Length)
+ throw new ArgumentOutOfRangeException("count");
+ if (String.IsNullOrWhiteSpace(Token))
+ throw new InvalidOperationException("Invalid Token");
+ if (StorageUrl == null)
+ throw new InvalidOperationException("Invalid Storage Url");
+ Contract.EndContractBlock();
+
+
+ //Don't use a timeout because putting the hashmap may be a long process
+ var client = new RestClient(_baseClient) { Timeout = 0 };
+ if (!String.IsNullOrWhiteSpace(account))
+ client.BaseAddress = GetAccountUrl(account);
+
+ var builder = client.GetAddressBuilder(container, "");
+ //We are doing an update
+ builder.Query = "update";
+ var uri = builder.Uri;
+
+ client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
+
+ Log.InfoFormat("[BLOCK POST] START");
+
+ client.UploadProgressChanged += (sender, args) =>
+ Log.InfoFormat("[BLOCK POST PROGRESS] {0}% {1} of {2}",
+ args.ProgressPercentage, args.BytesSent,
+ args.TotalBytesToSend);
+ client.UploadFileCompleted += (sender, args) =>
+ Log.InfoFormat("[BLOCK POST PROGRESS] Completed ");