X-Git-Url: https://code.grnet.gr/git/pithos-ms-client/blobdiff_plain/f6e0007d709886cb9f5e69962a0cd7b4a216d806..cfb091032c1ea51d109dccf68322ce2ee63eaab5:/trunk/Pithos.Network/CloudFilesClient.cs diff --git a/trunk/Pithos.Network/CloudFilesClient.cs b/trunk/Pithos.Network/CloudFilesClient.cs index 677b507..cef59f5 100644 --- a/trunk/Pithos.Network/CloudFilesClient.cs +++ b/trunk/Pithos.Network/CloudFilesClient.cs @@ -118,30 +118,6 @@ namespace Pithos.Network public Uri RootAddressUri { get; set; } - /* private WebProxy _proxy; - public WebProxy Proxy - { - get { return _proxy; } - set - { - _proxy = value; - if (_baseClient != null) - _baseClient.Proxy = value; - } - } -*/ - - /* private Uri _proxy; - public Uri Proxy - { - get { return _proxy; } - set - { - _proxy = value; - if (_baseClient != null) - _baseClient.Proxy = new WebProxy(value); - } - }*/ public double DownloadPercentLimit { get; set; } public double UploadPercentLimit { get; set; } @@ -243,17 +219,11 @@ namespace Pithos.Network var groups = new List(); using (var authClient = new HttpClient(_httpClientHandler,false){ BaseAddress = new Uri(AuthenticationUrl),Timeout=TimeSpan.FromSeconds(30) }) - //using (var authClient = new RestClient{BaseAddress=AuthenticationUrl}) { - /* if (Proxy != null) - authClient.Proxy = Proxy;*/ - - //Contract.Assume(authClient.DefaultRequestHeaders != null); authClient.DefaultRequestHeaders.Add("X-Auth-User", UserName); authClient.DefaultRequestHeaders.Add("X-Auth-Key", ApiKey); - //Func> call = () => ; string storageUrl; string token; @@ -261,11 +231,11 @@ namespace Pithos.Network { AssertStatusOK(response,"Authentication failed"); - storageUrl = response.Headers.GetValues("X-Storage-Url").First(); + storageUrl = response.Headers.GetFirstValue("X-Storage-Url"); if (String.IsNullOrWhiteSpace(storageUrl)) throw new InvalidOperationException("Failed to obtain storage url"); - token = response.Headers.GetValues(TOKEN_HEADER).First(); + token = response.Headers.GetFirstValue(TOKEN_HEADER); if (String.IsNullOrWhiteSpace(token)) throw new InvalidOperationException("Failed to obtain token url"); @@ -277,7 +247,6 @@ namespace Pithos.Network BaseAddress = storageUrl, Timeout = 30000, Retries = 3, - //Proxy=Proxy }; StorageUrl = new Uri(storageUrl); @@ -328,7 +297,7 @@ namespace Pithos.Network Log.InfoFormat("[{0}] {1} {2}", method, DateTime.Now, actualAddress); } - private async Task GetStringAsync(Uri targetUri, string errorMessage,DateTime? since=null) + private async Task GetStringAsync(Uri targetUri, string errorMessage,DateTimeOffset? since=null) { TraceStart("GET",targetUri); var request = new HttpRequestMessage(HttpMethod.Get, targetUri); @@ -336,7 +305,6 @@ namespace Pithos.Network { request.Headers.IfModifiedSince = since.Value; } - //Func> call = () => _baseHttpClient.SendAsync(request); using (var response = await _baseHttpClient.SendAsyncWithRetries(request,3).ConfigureAwait(false)) { AssertStatusOK(response, errorMessage); @@ -365,43 +333,7 @@ namespace Pithos.Network return infos; } - //public IList ListContainers(string account) - //{ - - // var targetUrl = GetTargetUrl(account); - // var targetUri=new Uri(String.Format("{0}?format=json",targetUrl)); - // var result = GetStringAsync(targetUri, "List Containers failed").Result; - // if (String.IsNullOrWhiteSpace(result)) - // return new List(); - // var infos = JsonConvert.DeserializeObject>(result); - // foreach (var info in infos) - // { - // info.Account = account; - // } - // return infos; - // /* using (var client = new RestClient(_baseClient)) - // { - // if (!String.IsNullOrWhiteSpace(account)) - // client.BaseAddress = GetAccountUrl(account); - - // client.Parameters.Clear(); - // client.Parameters.Add("format", "json"); - // var content = client.DownloadStringWithRetryRelative(_emptyUri, 3); - // client.AssertStatusOK("List Containers failed"); - - // if (client.StatusCode == HttpStatusCode.NoContent) - // return new List(); - // var infos = JsonConvert.DeserializeObject>(content); - - // foreach (var info in infos) - // { - // info.Account = account; - // } - // return infos; - // } */ - - //} - + private string GetAccountUrl(string account) { return RootAddressUri.Combine(account).AbsoluteUri; @@ -424,30 +356,6 @@ namespace Pithos.Network Log.DebugFormat("END"); return infos; -/* - using (var client = new RestClient(_baseClient)) - { - client.Parameters.Clear(); - client.Parameters.Add("format", "json"); - client.IfModifiedSince = since; - - //Extract the username from the base address - client.BaseAddress = RootAddressUri.AbsoluteUri; - - var content = client.DownloadStringWithRetryRelative(_emptyUri, 3); - - client.AssertStatusOK("ListSharingAccounts failed"); - - //If the result is empty, return an empty list, - var infos = String.IsNullOrWhiteSpace(content) - ? new List() - //Otherwise deserialize the account list into a list of ShareAccountInfos - : JsonConvert.DeserializeObject>(content); - - Log.DebugFormat("END"); - return infos; - } -*/ } } @@ -461,7 +369,7 @@ namespace Pithos.Network /// /// /// - public IList ListSharedObjects(HashSet knownContainers,DateTime? since = null ) + public IList ListSharedObjects(HashSet knownContainers, DateTimeOffset? since) { using (ThreadContext.Stacks["Share"].Push("List Objects")) @@ -662,7 +570,7 @@ namespace Pithos.Network } - public AccountInfo GetAccountPolicies(AccountInfo accountInfo) + public async Task GetAccountPolicies(AccountInfo accountInfo) { if (accountInfo==null) throw new ArgumentNullException("accountInfo"); @@ -672,6 +580,7 @@ namespace Pithos.Network { if (Log.IsDebugEnabled) Log.DebugFormat("START"); +/* if (_baseClient == null) { _baseClient = new RestClient @@ -682,28 +591,46 @@ namespace Pithos.Network }; } - using (var client = new RestClient(_baseClient)) +*/ + var containerUri = GetTargetUri(accountInfo.UserName); + var targetUri = new Uri(String.Format("{0}?format=json", containerUri), UriKind.Absolute); + using(var response=await _baseHttpClient.HeadAsyncWithRetries(targetUri,3).ConfigureAwait(false)) { - if (!String.IsNullOrWhiteSpace(accountInfo.UserName)) - client.BaseAddress = GetAccountUrl(accountInfo.UserName); - - client.Parameters.Clear(); - client.Parameters.Add("format", "json"); - client.Head(_emptyUri, 3); - - var quotaValue=client.ResponseHeaders["X-Account-Policy-Quota"]; - var bytesValue= client.ResponseHeaders["X-Account-Bytes-Used"]; - + + var quotaValue=response.Headers.GetFirstValue("X-Account-Policy-Quota"); + var bytesValue = response.Headers.GetFirstValue("X-Account-Bytes-Used"); long quota, bytes; if (long.TryParse(quotaValue, out quota)) accountInfo.Quota = quota; if (long.TryParse(bytesValue, out bytes)) accountInfo.BytesUsed = bytes; - - return accountInfo; + return accountInfo; } + + //using (var client = new RestClient(_baseClient)) + //{ + // if (!String.IsNullOrWhiteSpace(accountInfo.UserName)) + // client.BaseAddress = GetAccountUrl(accountInfo.UserName); + + // client.Parameters.Clear(); + // client.Parameters.Add("format", "json"); + // client.Head(_emptyUri, 3); + + // var quotaValue=client.ResponseHeaders["X-Account-Policy-Quota"]; + // var bytesValue= client.ResponseHeaders["X-Account-Bytes-Used"]; + + // long quota, bytes; + // if (long.TryParse(quotaValue, out quota)) + // accountInfo.Quota = quota; + // if (long.TryParse(bytesValue, out bytes)) + // accountInfo.BytesUsed = bytes; + + // return accountInfo; + + //} + } } @@ -745,15 +672,9 @@ namespace Pithos.Network client.Headers.Add("X-Object-Public", isPublic); - /*var uriBuilder = client.GetAddressBuilder(objectInfo.Container, objectInfo.Name); - uriBuilder.Query = "update="; - var uri = uriBuilder.Uri.MakeRelativeUri(this.RootAddressUri);*/ var address = String.Format("{0}/{1}?update=",objectInfo.Container, objectInfo.Name); client.PostWithRetry(new Uri(address,UriKind.Relative),"application/xml"); - //client.UploadValues(uri,new NameValueCollection()); - - client.AssertStatusOK("UpdateMetadata failed"); //If the status is NOT ACCEPTED or OK we have a problem if (!(client.StatusCode == HttpStatusCode.Accepted || client.StatusCode == HttpStatusCode.OK)) @@ -826,7 +747,7 @@ namespace Pithos.Network - public IList ListObjects(string account, Uri container, DateTime? since = null) + public IList ListObjects(string account, Uri container, DateTimeOffset? since = null) { if (container==null) throw new ArgumentNullException("container"); @@ -836,30 +757,6 @@ namespace Pithos.Network using (ThreadContext.Stacks["Objects"].Push("List")) { - /*if (Log.IsDebugEnabled) Log.DebugFormat("START"); - - var targetUrl = String.IsNullOrWhiteSpace(account) - ? _baseHttpClient.BaseAddress.ToString() - : GetAccountUrl(account); - var targetUri = new Uri(String.Format("{0}?format=json", targetUrl)); - var result = GetString(targetUri, "ListObjects failed",since).Result; - - if (String.IsNullOrWhiteSpace(result)) - return new[]{new NoModificationInfo(account,container)}; - //If the result is empty, return an empty list, - var infos = String.IsNullOrWhiteSpace(result) - ? new List() - //Otherwise deserialize the object list into a list of ObjectInfos - : JsonConvert.DeserializeObject>(result); - - foreach (var info in infos) - { - info.Container = container; - info.Account = account; - info.StorageUri = StorageUrl; - } - if (Log.IsDebugEnabled) Log.DebugFormat("END"); - return infos;*/ var containerUri = GetTargetUri(account).Combine(container); var targetUri = new Uri(String.Format("{0}?format=json", containerUri), UriKind.Absolute); @@ -884,50 +781,15 @@ namespace Pithos.Network } if (Log.IsDebugEnabled) Log.DebugFormat("END"); return infos; -/* - using (var client = new RestClient(_baseClient)) - { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - client.Parameters.Clear(); - client.Parameters.Add("format", "json"); - client.IfModifiedSince = since; - var content = client.DownloadStringWithRetryRelative(container, 3); - - client.AssertStatusOK("ListObjects failed"); - - if (client.StatusCode == HttpStatusCode.NotModified) - return new[] {new NoModificationInfo(account, container)}; - //If the result is empty, return an empty list, - var infos = String.IsNullOrWhiteSpace(content) - ? new List() - //Otherwise deserialize the object list into a list of ObjectInfos - : JsonConvert.DeserializeObject>(content); - - foreach (var info in infos) - { - info.Container = container; - info.Account = account; - info.StorageUri = StorageUrl; - } - if (Log.IsDebugEnabled) Log.DebugFormat("END"); - return infos; - } -*/ } } - public IList ListObjects(string account, Uri container, Uri folder, DateTime? since = null) + public IList ListObjects(string account, Uri container, Uri folder, DateTimeOffset? since = null) { if (container==null) throw new ArgumentNullException("container"); if (container.IsAbsoluteUri) throw new ArgumentException("container"); -/* - if (String.IsNullOrWhiteSpace(folder)) - throw new ArgumentNullException("folder"); -*/ Contract.EndContractBlock(); using (ThreadContext.Stacks["Objects"].Push("List")) @@ -1114,22 +976,86 @@ namespace Pithos.Network } - public ObjectInfo GetObjectInfo(string account, Uri container, Uri objectName) + public async Task GetObjectInfo(string account, Uri container, Uri objectName) { if (container == null) throw new ArgumentNullException("container", "The container property can't be empty"); if (container.IsAbsoluteUri) - throw new ArgumentException("The container must be relative","container"); + throw new ArgumentException("The container must be relative", "container"); if (objectName == null) throw new ArgumentNullException("objectName", "The objectName property can't be empty"); if (objectName.IsAbsoluteUri) - throw new ArgumentException("The objectName must be relative","objectName"); + throw new ArgumentException("The objectName must be relative", "objectName"); Contract.EndContractBlock(); using (ThreadContext.Stacks["Objects"].Push("GetObjectInfo")) - { + { - using (var client = new RestClient(_baseClient)) + var targetUri = GetTargetUri(account).Combine(container).Combine(objectName); + try + { + using (var response = await _baseHttpClient.HeadAsyncWithRetries(targetUri, 3,true)) + { + switch (response.StatusCode) + { + case HttpStatusCode.OK: + case HttpStatusCode.NoContent: + var tags = response.Headers.GetMeta("X-Object-Meta-"); + var extensions = (from header in response.Headers + where + header.Key.StartsWith("X-Object-") && + !header.Key.StartsWith("X-Object-Meta-") + select new {Name = header.Key, Value = header.Value.FirstOrDefault()}) + .ToDictionary(t => t.Name, t => t.Value); + + var permissions = response.Headers.GetFirstValue("X-Object-Sharing"); + + + var info = new ObjectInfo + { + Account = account, + Container = container, + Name = objectName, + ETag = response.Headers.ETag.NullSafe(e=>e.Tag), + UUID = response.Headers.GetFirstValue("X-Object-UUID"), + X_Object_Hash = response.Headers.GetFirstValue("X-Object-Hash"), + Content_Type = response.Headers.GetFirstValue("Content-Type"), + Bytes = Convert.ToInt64(response.Content.Headers.ContentLength), + Tags = tags, + Last_Modified = response.Content.Headers.LastModified, + Extensions = extensions, + ContentEncoding = + response.Content.Headers.ContentEncoding.FirstOrDefault(), + ContendDisposition = + response.Content.Headers.ContentDisposition.NullSafe(c=>c.ToString()), + Manifest = response.Headers.GetFirstValue("X-Object-Manifest"), + PublicUrl = response.Headers.GetFirstValue("X-Object-Public"), + StorageUri = StorageUrl, + }; + info.SetPermissions(permissions); + return info; + case HttpStatusCode.NotFound: + return ObjectInfo.Empty; + default: + throw new WebException( + String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}", + objectName, response.StatusCode)); + } + } + } + catch (RetryException) + { + Log.WarnFormat("[RETRY FAIL] GetObjectInfo for {0} failed.", objectName); + return ObjectInfo.Empty; + } + catch (WebException e) + { + Log.Error( + String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status {1}", + objectName, e.Status), e); + throw; + } + } /* using (var client = new RestClient(_baseClient)) { if (!String.IsNullOrWhiteSpace(account)) client.BaseAddress = GetAccountUrl(account); @@ -1198,12 +1124,12 @@ namespace Pithos.Network objectName, client.StatusCode), e); throw; } - } - } + } */ + } - + public void CreateFolder(string account, Uri container, Uri folder) { @@ -1245,7 +1171,21 @@ namespace Pithos.Network */ } - + private Dictionary GetMeta(HttpResponseMessage response,string metaPrefix) + { + if (String.IsNullOrWhiteSpace(metaPrefix)) + throw new ArgumentNullException("metaPrefix"); + Contract.EndContractBlock(); + + var dict = (from header in response.Headers + where header.Key.StartsWith(metaPrefix) + select new { Name = header.Key, Value = String.Join(",", header.Value) }) + .ToDictionary(t => t.Name, t => t.Value); + + + return dict; + } + public ContainerInfo GetContainerInfo(string account, Uri container) { @@ -1255,42 +1195,41 @@ namespace Pithos.Network throw new ArgumentException("The container must be relative","container"); Contract.EndContractBlock(); - using (var client = new RestClient(_baseClient)) + var targetUri = GetTargetUri(account).Combine(container); + using (var response = _baseHttpClient.HeadAsyncWithRetries(targetUri, 3).Result) { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - client.Head(container); - switch (client.StatusCode) + if (Log.IsDebugEnabled) + Log.DebugFormat("ContainerInfo data: {0}\n{1}",response,response.Content.ReadAsStringAsync().Result); + switch (response.StatusCode) { case HttpStatusCode.OK: case HttpStatusCode.NoContent: - var tags = client.GetMeta("X-Container-Meta-"); - var policies = client.GetMeta("X-Container-Policy-"); + var tags = GetMeta(response,"X-Container-Meta-"); + var policies = GetMeta(response,"X-Container-Policy-"); var containerInfo = new ContainerInfo { - Account=account, + Account = account, Name = container, - StorageUrl=StorageUrl.ToString(), - Count = - long.Parse(client.GetHeaderValue("X-Container-Object-Count")), - Bytes = long.Parse(client.GetHeaderValue("X-Container-Bytes-Used")), - BlockHash = client.GetHeaderValue("X-Container-Block-Hash"), - BlockSize=int.Parse(client.GetHeaderValue("X-Container-Block-Size")), - Last_Modified=client.LastModified, - Tags=tags, - Policies=policies + StorageUrl = StorageUrl.ToString(), + Count =long.Parse(response.Headers.GetFirstValue("X-Container-Object-Count")), + Bytes = long.Parse(response.Headers.GetFirstValue("X-Container-Bytes-Used")), + BlockHash = response.Headers.GetFirstValue("X-Container-Block-Hash"), + BlockSize = + int.Parse(response.Headers.GetFirstValue("X-Container-Block-Size")), + Last_Modified = response.Content.Headers.LastModified, + Tags = tags, + Policies = policies }; - + return containerInfo; case HttpStatusCode.NotFound: return ContainerInfo.Empty; default: - throw CreateWebException("GetContainerInfo", client.StatusCode); + throw CreateWebException("GetContainerInfo", response.StatusCode); } - } + } } public void CreateContainer(string account, Uri container) @@ -1324,7 +1263,7 @@ namespace Pithos.Network */ } - public void WipeContainer(string account, Uri container) + public async Task WipeContainer(string account, Uri container) { if (container == null) throw new ArgumentNullException("container", "The container property can't be empty"); @@ -1332,11 +1271,11 @@ namespace Pithos.Network throw new ArgumentException("The container must be relative", "container"); Contract.EndContractBlock(); - DeleteContainer(account, new Uri(String.Format("{0}&delimiter=//", container), UriKind.Relative)); + await DeleteContainer(account, new Uri(String.Format("{0}?delimiter=/", container), UriKind.Relative)).ConfigureAwait(false); } - public void DeleteContainer(string account, Uri container) + public async Task DeleteContainer(string account, Uri container) { if (container == null) throw new ArgumentNullException("container", "The container property can't be empty"); @@ -1346,24 +1285,12 @@ namespace Pithos.Network var targetUri = GetTargetUri(account).Combine(container); var message = new HttpRequestMessage(HttpMethod.Delete, targetUri); - using (var response = _baseHttpClient.SendAsyncWithRetries(message, 3).Result) + using (var response = await _baseHttpClient.SendAsyncWithRetries(message, 3).ConfigureAwait(false)) { var expectedCodes = new[] { HttpStatusCode.NotFound, HttpStatusCode.NoContent }; if (!expectedCodes.Contains(response.StatusCode)) throw CreateWebException("DeleteContainer", response.StatusCode); } -/* - using (var client = new RestClient(_baseClient)) - { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - client.DeleteWithRetry(container, 3); - var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent}; - if (!expectedCodes.Contains(client.StatusCode)) - throw CreateWebException("DeleteContainer", client.StatusCode); - } -*/ } @@ -1517,101 +1444,6 @@ namespace Pithos.Network } -/* - public Task> PutHashMap(string account, Uri container, Uri objectName, TreeHash hash) - { - if (container == null) - throw new ArgumentNullException("container", "The container property can't be empty"); - if (container.IsAbsoluteUri) - throw new ArgumentException("The container must be relative","container"); - if (objectName == null) - throw new ArgumentNullException("objectName", "The objectName property can't be empty"); - if (objectName.IsAbsoluteUri) - throw new ArgumentException("The objectName must be relative","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 jsonHash = hash.ToJson(); - - client.Headers.Add("ETag",hash.TopHash.ToHashString()); - var uploadTask=client.UploadStringTask(uri, "PUT", jsonHash); - if (Log.IsDebugEnabled) - Log.DebugFormat("Hashes:\r\n{0}", jsonHash); - return uploadTask.ContinueWith(t => - { - - var empty = (IList)new List(); - - - //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=stream.GetLoggedReader(Log)) - { - //We used to have to cleanup the content before returning it because it contains - //error content after the list of hashes - // - //As of 30/1/2012, the result is a proper Json array so we don't need to read the content - //line by line - - var serializer = new JsonSerializer(); - serializer.Error += (sender, args) => Log.ErrorFormat("Deserialization error at [{0}] [{1}]", args.ErrorContext.Error, args.ErrorContext.Member); - var hashes = (List)serializer.Deserialize(reader, typeof(List)); - return hashes; - } - } - //Any other status code is unexpected and the exception should be rethrown - Log.LogError(response); - throw ex; - - } - - //Any other status code is unexpected but there was no exception. We can probably continue processing - Log.WarnFormat("Unexcpected status code when putting map: {0} - {1}",client.StatusCode,client.StatusDescription); - - return empty; - }); - - } - -*/ - - public async Task GetBlock(string account, Uri container, Uri relativeUrl, long start, long? end, CancellationToken cancellationToken) { @@ -1634,7 +1466,7 @@ namespace Pithos.Network var targetUri = GetTargetUri(account).Combine(container).Combine(relativeUrl); var message = new HttpRequestMessage(HttpMethod.Get, targetUri); - message.Headers.Range.Ranges.Add(new RangeItemHeaderValue(start,end)); + message.Headers.Range=new RangeHeaderValue(start,end); //Don't use a timeout because putting the hashmap may be a long process @@ -1650,7 +1482,7 @@ namespace Pithos.Network }); - using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3, HttpCompletionOption.ResponseHeadersRead, + using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3, false,HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) using(var targetStream=new MemoryStream()) @@ -1670,36 +1502,7 @@ namespace Pithos.Network var result = targetStream.ToArray(); return result; } - /*using (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); - var uri = builder.Uri; - -/* client.DownloadProgressChanged += (sender, args) => - { - Log.DebugFormat("[GET PROGRESS] {0} {1}% {2} of {3}", - uri.Segments.Last(), args.ProgressPercentage, - args.BytesReceived, - args.TotalBytesToReceive); - DownloadProgressChanged(sender, args); - };#1# - var progress = new Progress(args => - { - Log.DebugFormat("[GET PROGRESS] {0} {1}% {2} of {3}", - uri.Segments.Last(), args.ProgressPercentage, - args.BytesReceived, - args.TotalBytesToReceive); - if (DownloadProgressChanged!=null) - DownloadProgressChanged(this, args); - }); - - - var result = await client.DownloadDataTaskAsync(uri, cancellationToken,progress).ConfigureAwait(false); - return result; - }*/ + } public event EventHandler UploadProgressChanged; @@ -1754,7 +1557,7 @@ namespace Pithos.Network message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(@"application/octet-stream"); //Send the block - using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3).ConfigureAwait(false)) + using (var response = await _baseHttpClientNoTimeout.SendAsyncWithRetries(message, 3,false,HttpCompletionOption.ResponseContentRead,token).ConfigureAwait(false)) { Log.InfoFormat("[BLOCK POST PROGRESS] Completed "); response.EnsureSuccessStatusCode(); @@ -1764,39 +1567,7 @@ namespace Pithos.Network if (responseHash.Equals(cleanHash,StringComparison.OrdinalIgnoreCase)) Log.ErrorFormat("Block hash mismatch posting to [{0}]:[{1}], expected [{2}] but was [{3}]",account,container,blockHash,responseHash); } - Log.InfoFormat("[BLOCK POST] END"); - /*using (var client = new RestClient(_baseClient) { Timeout = 0 }) - { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - var builder = client.GetAddressBuilder(container, _emptyUri); - //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.UploadFileCompleted += (sender, args) => - Log.InfoFormat("[BLOCK POST PROGRESS] Completed "); - - var progress=new Progress(args=> - { - Log.InfoFormat("[BLOCK POST PROGRESS] {0}% {1} of {2}", - args.ProgressPercentage, args.BytesSent, - args.TotalBytesToSend); - if (UploadProgressChanged!=null) - UploadProgressChanged(this, new UploadArgs(args)); - }); - var buffer = new byte[count]; - Buffer.BlockCopy(block, offset, buffer, 0, count); - //Send the block - - var response=await client.UploadDataTaskAsync(uri, "POST", buffer,token,progress).ConfigureAwait(false); - Log.InfoFormat("[BLOCK POST] END"); - }*/ + Log.InfoFormat("[BLOCK POST] END"); } catch (TaskCanceledException ) { @@ -1838,26 +1609,7 @@ namespace Pithos.Network var treeHash = TreeHash.Parse(json); Log.InfoFormat("[GET HASH] END {0}", objectName); return treeHash; - /* - using (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; - - var json = await client.DownloadStringTaskAsync(uri).ConfigureAwait(false); - var treeHash = TreeHash.Parse(json); - Log.InfoFormat("[GET HASH] END {0}", objectName); - return treeHash; - } - */ } catch (Exception exc) { @@ -1890,11 +1642,6 @@ namespace Pithos.Network throw new ArgumentException("The objectName must be relative","objectName"); if (String.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException("fileName", "The fileName property can't be empty"); -/* - if (!File.Exists(fileName) && !Directory.Exists(fileName)) - throw new FileNotFoundException("The file or directory does not exist",fileName); -*/ - try { @@ -1947,27 +1694,6 @@ namespace Pithos.Network } } - - -/* - private static string CalculateHash(string fileName) - { - Contract.Requires(!String.IsNullOrWhiteSpace(fileName)); - Contract.EndContractBlock(); - - string hash; - using (var hasher = MD5.Create()) - using(var stream=File.OpenRead(fileName)) - { - var hashBuilder=new StringBuilder(); - foreach (byte b in hasher.ComputeHash(stream)) - hashBuilder.Append(b.ToString("x2").ToLower()); - hash = hashBuilder.ToString(); - } - return hash; - } -*/ - public void MoveObject(string account, Uri sourceContainer, Uri oldObjectName, Uri targetContainer, Uri newObjectName) { @@ -2001,23 +1727,9 @@ namespace Pithos.Network if (!expectedCodes.Contains(response.StatusCode)) throw CreateWebException("MoveObject", response.StatusCode); } -/* - using (var client = new RestClient(_baseClient)) - { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - client.Headers.Add("X-Move-From", sourceUri.ToString()); - client.PutWithRetry(targetUri, 3); - - var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created}; - if (!expectedCodes.Contains(client.StatusCode)) - throw CreateWebException("MoveObject", client.StatusCode); - } -*/ } - public void DeleteObject(string account, Uri sourceContainer, Uri objectName, bool isDirectory) + public async Task DeleteObject(string account, Uri sourceContainer, Uri objectName, bool isDirectory) { if (sourceContainer == null) throw new ArgumentNullException("sourceContainer", "The sourceContainer property can't be empty"); @@ -2029,11 +1741,53 @@ namespace Pithos.Network throw new ArgumentException("The objectName must be relative","objectName"); Contract.EndContractBlock(); + + + var sourceUri = new Uri(String.Format("/{0}/{1}", sourceContainer, objectName),UriKind.Relative); + + + if (objectName.OriginalString.EndsWith(".ignore")) + using(var response = await _baseHttpClient.DeleteAsync(sourceUri)){} + else + { + var relativeUri = new Uri(String.Format("{0}/{1}", FolderConstants.TrashContainer, objectName), + UriKind.Relative); + +/* + var relativeUri = isDirectory + ? new Uri( + String.Format("{0}/{1}?delimiter=/", FolderConstants.TrashContainer, + objectName), UriKind.Relative) + : new Uri(String.Format("{0}/{1}", FolderConstants.TrashContainer, objectName), + UriKind.Relative); + +*/ + var targetUri = GetTargetUri(account).Combine(relativeUri); + + + var message = new HttpRequestMessage(HttpMethod.Put, targetUri); + message.Headers.Add("X-Move-From", sourceUri.ToString()); + + Log.InfoFormat("[TRASH] [{0}] to [{1}]", sourceUri, targetUri); + using (var response = await _baseHttpClient.SendAsyncWithRetries(message, 3)) + { + var expectedCodes = new[] + { + HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created, + HttpStatusCode.NotFound + }; + if (!expectedCodes.Contains(response.StatusCode)) + throw CreateWebException("DeleteObject", response.StatusCode); + } + } +/* + + var targetUrl = FolderConstants.TrashContainer + "/" + objectName; /* if (isDirectory) targetUrl = targetUrl + "?delimiter=/"; -*/ +#1# var sourceUrl = String.Format("/{0}/{1}", sourceContainer, objectName); @@ -2051,6 +1805,7 @@ namespace Pithos.Network if (!expectedCodes.Contains(client.StatusCode)) throw CreateWebException("DeleteObject", client.StatusCode); } +*/ } @@ -2060,46 +1815,34 @@ namespace Pithos.Network } -/* - public IEnumerable ListDirectories(ContainerInfo container) - { - var directories=this.ListObjects(container.Account, container.Name, "/"); - } -*/ - - public bool CanUpload(string account, ObjectInfo cloudFile) + public async Task CanUpload(string account, ObjectInfo cloudFile) { Contract.Requires(!String.IsNullOrWhiteSpace(account)); Contract.Requires(cloudFile!=null); - using (var client = new RestClient(_baseClient)) - { - if (!String.IsNullOrWhiteSpace(account)) - client.BaseAddress = GetAccountUrl(account); - - var parts = cloudFile.Name.ToString().Split('/'); var folder = String.Join("/", parts,0,parts.Length-1); var fileName = String.Format("{0}/{1}.pithos.ignore", folder, Guid.NewGuid()); var fileUri=fileName.ToEscapedUri(); - client.Parameters.Clear(); try { var relativeUri = cloudFile.Container.Combine(fileUri); - client.PutWithRetry(relativeUri, 3, @"application/octet-stream"); - + var targetUri = GetTargetUri(account).Combine(relativeUri); + var message = new HttpRequestMessage(HttpMethod.Put, targetUri); + message.Content.Headers.ContentType =new MediaTypeHeaderValue("application/octet-stream"); + var response=await _baseHttpClient.SendAsyncWithRetries(message, 3); var expectedCodes = new[] { HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created}; - var result=(expectedCodes.Contains(client.StatusCode)); - DeleteObject(account, cloudFile.Container, fileUri, cloudFile.IsDirectory); + var result=(expectedCodes.Contains(response.StatusCode)); + await DeleteObject(account, cloudFile.Container, fileUri, cloudFile.IsDirectory); return result; } catch { return false; } - } + } ~CloudFilesClient()