Revision b5061ac8 trunk/Pithos.Network/CloudFilesClient.cs

b/trunk/Pithos.Network/CloudFilesClient.cs
8 8
using System.Net;
9 9
using System.Security.Cryptography;
10 10
using System.Text;
11
using System.Threading.Tasks;
11 12
using Hammock;
12 13
using Hammock.Caching;
13 14
using Hammock.Retries;
......
28 29
        private RestClient _client;
29 30
        private readonly TimeSpan _shortTimeout = TimeSpan.FromSeconds(10);
30 31
        private readonly int _retries = 5;
32
        private RetryPolicy _retryPolicy;
31 33
        public string ApiKey { get; set; }
32 34
        public string UserName { get; set; }
33 35
        public Uri StorageUrl { get; set; }
34 36
        public string Token { get; set; }
35 37
        public Uri Proxy { get; set; }
38

  
39
        public double DownloadPercentLimit { get; set; }
40
        public double UploadPercentLimit { get; set; }
36 41
        
37 42
        public string AuthUrl
38 43
        {
......
48 53

  
49 54
        public void Authenticate(string userName,string apiKey)
50 55
        {
56
            Trace.TraceInformation("[AUTHENTICATE] Start for {0}",userName);
51 57
            if (String.IsNullOrWhiteSpace(userName))
52 58
                throw new ArgumentNullException("userName","The userName property can't be empty");
53 59
            if (String.IsNullOrWhiteSpace(apiKey))
......
91 97
            else
92 98
                Token = "0000";
93 99

  
94
            var retryPolicy = new RetryPolicy { RetryCount = _retries };
95
            retryPolicy.RetryConditions.Add(new TimeoutRetryCondition());
100
            _retryPolicy = new RetryPolicy { RetryCount = _retries };
101
            _retryPolicy.RetryConditions.Add(new TimeoutRetryCondition());
96 102

  
97
            _client = new RestClient { Authority = StorageUrl.AbsoluteUri, Path = UserName, Proxy = proxy, RetryPolicy = retryPolicy, };
103
            _client = new RestClient { Authority = StorageUrl.AbsoluteUri, Path = UserName, Proxy = proxy };
104
            _client.FileProgress += OnFileProgress;
98 105
            
99 106
            _client.AddHeader("X-Auth-Token", Token);
100 107
            if (UsePithos)
......
103 110
                _client.AddHeader("X-Auth-Key",ApiKey);                
104 111
            }
105 112

  
113
            Trace.TraceInformation("[AUTHENTICATE] End for {0}", userName);
114
        }
106 115

  
116
        private void OnFileProgress(object sender, FileProgressEventArgs e)
117
        {
118
            Trace.TraceInformation("[PROGRESS] {0} {1:p} {2} of {3}",e.FileName,(double)e.BytesWritten/e.TotalBytes, e.BytesWritten,e.TotalBytes);            
107 119
        }
108 120

  
109 121
        public IList<ContainerInfo> ListContainers()
......
112 124
            //appends a / unless a Path is specified.
113 125
            
114 126
            //Create a request with a complete path
115
            var request = new RestRequest { Path = StorageUrl.ToString(), Timeout = _shortTimeout };
127
            var request = new RestRequest { Path = StorageUrl.ToString(), RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
116 128
            request.AddParameter("format","json");
117 129
            //Create a client clone
118 130
            var client = new RestClient{Proxy=Proxy.ToString()};
......
139 151
            if (String.IsNullOrWhiteSpace(container))
140 152
                throw new ArgumentNullException("container", "The container property can't be empty");
141 153

  
142
            var request = new RestRequest { Path = container, Timeout = _shortTimeout };
154
            Trace.TraceInformation("[START] ListObjects");
155

  
156
            var request = new RestRequest { Path = container, RetryPolicy = _retryPolicy, Timeout = TimeSpan.FromMinutes(1) };
143 157
            request.AddParameter("format", "json");
144 158
            var response = _client.Request(request);
145 159
            
146 160
            var infos = InfosFromContent(response);
147 161

  
162
            Trace.TraceInformation("[END] ListObjects");
148 163
            return infos;
149 164
        }
150 165

  
......
155 170
            if (String.IsNullOrWhiteSpace(container))
156 171
                throw new ArgumentNullException("container", "The container property can't be empty");
157 172

  
158
            var request = new RestRequest { Path = container, Timeout = _shortTimeout };
173
            Trace.TraceInformation("[START] ListObjects");
174

  
175
            var request = new RestRequest { Path = container,RetryPolicy = _retryPolicy, Timeout = TimeSpan.FromMinutes(1) };
159 176
            request.AddParameter("format", "json");
160 177
            request.AddParameter("path", folder);
161 178
            var response = _client.Request(request);
162 179
            
163 180
            var infos = InfosFromContent(response);
164 181

  
182
            Trace.TraceInformation("[END] ListObjects");
165 183
            return infos;
166 184
        }
167 185

  
......
193 211
            if (String.IsNullOrWhiteSpace(container))
194 212
                throw new ArgumentNullException("container", "The container property can't be empty");
195 213

  
196
            var request = new RestRequest { Path = container, Method = WebMethod.Head, Timeout = _shortTimeout };
214
            var request = new RestRequest { Path = container, Method = WebMethod.Head, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
197 215
            var response = _client.Request(request);
198 216

  
199 217
            switch(response.StatusCode)
......
215 233
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
216 234

  
217 235

  
218
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head, Timeout = _shortTimeout };
236
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head,RetryPolicy = _retryPolicy, Timeout = _shortTimeout };
219 237
            var response = _client.Request(request);
220 238

  
221 239
            switch (response.StatusCode)
......
239 257
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
240 258

  
241 259

  
242
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head, Timeout = _shortTimeout };
260
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
243 261
            var response = _client.Request(request);
244 262

  
245 263
            if (response.TimedOut)
......
252 270
                    var keys = response.Headers.AllKeys.AsQueryable();
253 271
                    return new ObjectInfo
254 272
                               {
255
                                   Name=objectName,
273
                                   Name = objectName,
256 274
                                   Bytes = long.Parse(GetHeaderValue("Content-Length", response, keys)),
257 275
                                   Hash = GetHeaderValue("ETag", response, keys),
258 276
                                   Content_Type = GetHeaderValue("Content-Type", response, keys)
......
260 278
                case HttpStatusCode.NotFound:
261 279
                    return ObjectInfo.Empty;
262 280
                default:
263
                        throw new WebException(String.Format("GetObjectInfo failed with unexpected status code {0}", response.StatusCode));
281
                    if (request.RetryState.RepeatCount > 0)
282
                    {
283
                        Trace.TraceWarning("[RETRY FAIL] GetObjectInfo for {0} failed after {1} retries",
284
                                                      objectName, request.RetryState.RepeatCount);
285
                        return ObjectInfo.Empty;
286
                    }
287
                    if (response.InnerException != null)
288
                        throw new WebException(String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}", objectName, response.StatusCode), response.InnerException);
289
                    throw new WebException(String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}", objectName, response.StatusCode));
264 290
            }
265 291
        }
266 292

  
......
272 298
                throw new ArgumentNullException("folder", "The folder property can't be empty");
273 299

  
274 300
            var folderUrl=String.Format("{0}/{1}",container,folder);
275
            var request = new RestRequest { Path = folderUrl, Method = WebMethod.Put, Timeout = _shortTimeout };
301
            var request = new RestRequest { Path = folderUrl, Method = WebMethod.Put, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
276 302
            request.AddHeader("Content-Type", @"application/directory");
277 303
            request.AddHeader("Content-Length", "0");
278 304

  
......
288 314
            if (String.IsNullOrWhiteSpace(container))
289 315
                throw new ArgumentNullException("container", "The container property can't be empty");
290 316

  
291
            var request = new RestRequest { Path = container, Method = WebMethod.Head, Timeout = _shortTimeout };
317
            var request = new RestRequest { Path = container, Method = WebMethod.Head, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
292 318
            var response = _client.Request(request);
293 319

  
294 320
            switch(response.StatusCode)
......
314 340
            if (String.IsNullOrWhiteSpace(container))
315 341
                throw new ArgumentNullException("container", "The container property can't be empty");
316 342

  
317
            var request = new RestRequest { Path = container, Method = WebMethod.Put, Timeout = _shortTimeout };
343
            var request = new RestRequest { Path = container, Method = WebMethod.Put, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
318 344
            
319 345
            var response = _client.Request(request);
320 346
                        
......
327 353
            if (String.IsNullOrWhiteSpace(container))
328 354
                throw new ArgumentNullException("container", "The container property can't be empty");
329 355

  
330
            var request = new RestRequest { Path = container, Method = WebMethod.Delete, Timeout = _shortTimeout };
356
            var request = new RestRequest { Path = container, Method = WebMethod.Delete, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
331 357
            var response = _client.Request(request);
332 358

  
333 359
            if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.NoContent)
......
352 378
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
353 379

  
354 380
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Get };
381
/*
382
            if (DownloadPercentLimit > 0)
383
                request.TaskOptions = new TaskOptions<int> { RateLimitPercent = DownloadPercentLimit };
384
*/
385
            
355 386
            var response = _client.Request(request);
356 387
            
357 388
            if (response.StatusCode == HttpStatusCode.NotFound)
......
370 401
        /// <param name="container"></param>
371 402
        /// <param name="objectName"></param>
372 403
        /// <param name="fileName"></param>
404
        /// <param name="hash">Optional hash value for the file. If no hash is provided, the method calculates a new hash</param>
373 405
        /// <remarks>>This method should have no timeout or a very long one</remarks>
374
        public void PutObject(string container, string objectName, string fileName)
406
        public Task PutObject(string container, string objectName, string fileName, string hash = null)
375 407
        {
376 408
            if (String.IsNullOrWhiteSpace(container))
377 409
                throw new ArgumentNullException("container", "The container property can't be empty");
......
386 418
            string url = container + "/" + objectName;
387 419

  
388 420
            var request = new RestRequest {Path=url,Method=WebMethod.Put};           
389
            request.TaskOptions=new TaskOptions<int>{RateLimitPercent=0.5};
390 421
            
391
            string hash = CalculateHash(fileName);
392

  
393
            request.AddPostContent(File.ReadAllBytes(fileName));
422
/*
423
            if(UploadPercentLimit>0)
424
                request.TaskOptions=new TaskOptions<int>{RateLimitPercent=UploadPercentLimit};
425
*/
426
            Trace.TraceInformation("[PUT] START {0}",objectName);
427
            string etag = hash??CalculateHash(fileName);
428
            request.AddFile(fileName, fileName, fileName);            
429
            //request.AddPostContent(File.ReadAllBytes(fileName));
394 430
            request.AddHeader("Content-Type","application/octet-stream");
395
            request.AddHeader("ETag",hash);
396
            var response=_client.Request(request);
397
            _client.TaskOptions = new TaskOptions<int> {RateLimitPercent = 0.5};
398
            if (response.StatusCode == HttpStatusCode.Created)
399
                return;
400
            if (response.StatusCode == HttpStatusCode.LengthRequired)
401
                throw new InvalidOperationException();
402
            else
403
                throw new WebException(String.Format("GetObject failed with unexpected status code {0}", response.StatusCode));
431
            request.AddHeader("ETag", etag);
432
            //_client.TaskOptions = new TaskOptions<int> {RateLimitPercent = 0.5};
433
            try
434
            {
435

  
436
                var response=_client.Request(request);
437
                Trace.TraceInformation("[PUT] END {0}", objectName);
438

  
439
                if (response.StatusCode == HttpStatusCode.Created)
440
                    return Task.Factory.StartNew(()=>{});
441
                if (response.StatusCode == HttpStatusCode.LengthRequired)
442
                    throw new InvalidOperationException();
443
                else
444
                    throw new WebException(String.Format("GetObject failed with unexpected status code {0}",
445
                            response.StatusCode));
446
                /*            return Task.Factory.FromAsync(_client.BeginRequest(request),ar=>_client.EndRequest(ar))
447
                                .ContinueWith(t=>
448
                                {*/
449
            }
450
            catch (Exception exc)
451
            {
452
                Trace.TraceError("[PUT] END {0} with {1}", objectName, exc);
453
                throw;
454
            }                
455

  
456
/*
457
            var response = t.Result;
458
                    if (t.IsFaulted)
459
                        Trace.TraceError("[PUT] END {0} with {1}", objectName, t.Exception);
460
                    else
461
                    {
462
                        Trace.TraceInformation("[PUT] END {0}",objectName);
463
                    }
464
*/
465
                  /*  if (response.StatusCode == HttpStatusCode.Created)
466
                        return;
467
                    if (response.StatusCode == HttpStatusCode.LengthRequired)
468
                        throw new InvalidOperationException();
469
                    else
470
                        throw new WebException(String.Format("GetObject failed with unexpected status code {0}",
471
                                response.StatusCode));*/
472
                /*});*/
404 473
        }
405 474

  
406 475
        private static string CalculateHash(string fileName)
......
424 493
            if (String.IsNullOrWhiteSpace(objectName))
425 494
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
426 495

  
427
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Delete, Timeout=_shortTimeout };
496
            var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Delete, RetryPolicy = _retryPolicy,Timeout = _shortTimeout };
428 497
            var response = _client.Request(request);
429 498

  
430 499
            if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.NoContent)

Also available in: Unified diff