5 <title>RestClient.cs</title>
6 <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
7 <link href="focco.css" rel="stylesheet" media="all" type="text/css" />
8 <script src="prettify.js" type="text/javascript"></script>
10 <body onload="prettyPrint()">
12 <div id="background"></div>
15 <div id="jump_wrapper">
17 <a class="source" href="cloudfilesclient.html">
20 <a class="source" href="restclient.html">
23 <a class="source" href="timeoutretrycondition.html">
24 TimeoutRetryCondition.cs
26 <a class="source" href="properties/assemblyinfo.html">
27 Properties\AssemblyInfo.cs
32 <table cellpadding="0" cellspacing="0">
36 <h1>RestClient.cs</h1>
38 <th class="code"></th>
45 <a class="pilcrow" href="#section_1">¶</a>
49 <p><copyright file="RestClient.cs" company="Microsoft">
50 TODO: Update copyright text.</p>
56 <pre><code class='prettyprint'>
57 using System.Collections.Specialized;
58 using System.Diagnostics;
59 using System.Diagnostics.Contracts;
62 using System.Runtime.Serialization;
63 using System.Threading.Tasks;
65 namespace Pithos.Network
68 using System.Collections.Generic;
78 <a class="pilcrow" href="#section_2">¶</a>
81 / TODO: Update summary.
86 <pre><code class='prettyprint'> public class RestClient:WebClient
88 public int Timeout { get; set; }
90 public bool TimedOut { get; set; }
92 public HttpStatusCode StatusCode { get; private set; }
94 public string StatusDescription { get; set; }
97 public int Retries { get; set; }
99 private readonly Dictionary<string, string> _parameters=new Dictionary<string, string>();
100 public Dictionary<string, string> Parameters
102 get { return _parameters; }
105 public RestClient():base()
111 public RestClient(RestClient other)
115 Timeout = other.Timeout;
116 Retries = other.Retries;
117 BaseAddress = other.BaseAddress;
119 foreach (var parameter in other.Parameters)
121 Parameters.Add(parameter.Key,parameter.Value);
124 this.Proxy = other.Proxy;
127 protected override WebRequest GetWebRequest(Uri address)
130 var webRequest = base.GetWebRequest(address);
131 var request = webRequest as HttpWebRequest;
132 if (IfModifiedSince.HasValue)
133 request.IfModifiedSince = IfModifiedSince.Value;
134 request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
136 request.Timeout = Timeout;
140 public DateTime? IfModifiedSince { get; set; }
142 protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
144 var response = (HttpWebResponse) base.GetWebResponse(request, result);
145 StatusCode=response.StatusCode;
146 StatusDescription=response.StatusDescription;
152 protected override WebResponse GetWebResponse(WebRequest request)
156 var response = (HttpWebResponse)base.GetWebResponse(request);
157 StatusCode = response.StatusCode;
158 StatusDescription = response.StatusDescription;
161 catch (WebException exc)
163 if (exc.Response!=null && exc.Response.ContentLength > 0)
165 string content = GetContent(exc.Response);
166 Trace.TraceError(content);
172 private static string GetContent(WebResponse webResponse)
175 using (var stream = webResponse.GetResponseStream())
176 using (var reader = new StreamReader(stream))
178 content = reader.ReadToEnd();
183 public string DownloadStringWithRetry(string address,int retries=0)
186 throw new ArgumentNullException("address");
188 var actualAddress = GetActualAddress(address);
190 TraceStart("GET",actualAddress);
192 var actualRetries = (retries == 0) ? Retries : retries;
196 var task = Retry(() =>
198 var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress);
199 var content = base.DownloadString(uriString);
201 if (StatusCode == HttpStatusCode.NoContent)
207 var result = task.Result;
211 public void Head(string address,int retries=0)
213 RetryWithoutContent(address, retries, "HEAD");
216 public void PutWithRetry(string address, int retries = 0)
218 RetryWithoutContent(address, retries, "PUT");
221 public void DeleteWithRetry(string address,int retries=0)
223 RetryWithoutContent(address, retries, "DELETE");
226 public string GetHeaderValue(string headerName)
228 var values=this.ResponseHeaders.GetValues(headerName);
230 throw new WebException(String.Format("The {0} header is missing", headerName));
235 private void RetryWithoutContent(string address, int retries, string method)
238 throw new ArgumentNullException("address");
240 var actualAddress = GetActualAddress(address);
241 var actualRetries = (retries == 0) ? Retries : retries;
243 var task = Retry(() =>
245 var uriString = String.Join("/",BaseAddress ,actualAddress);
246 var uri = new Uri(uriString);
247 var request = GetWebRequest(uri);
248 request.Method = method;
249 if (ResponseHeaders!=null)
250 ResponseHeaders.Clear();
252 TraceStart(method, uriString);
254 var response = (HttpWebResponse)GetWebResponse(request);
255 StatusCode = response.StatusCode;
256 StatusDescription = response.StatusDescription;
265 /*private string RetryWithContent(string address, int retries, string method)
268 throw new ArgumentNullException("address");
270 var actualAddress = GetActualAddress(address);
271 var actualRetries = (retries == 0) ? Retries : retries;
273 var task = Retry(() =>
275 var uriString = String.Join("/",BaseAddress ,actualAddress);
276 var uri = new Uri(uriString);
278 var request = GetWebRequest(uri);
279 request.Method = method;
281 if (ResponseHeaders!=null)
282 ResponseHeaders.Clear();
284 TraceStart(method, uriString);
286 var getResponse = request.GetResponseAsync();
288 var setStatus= getResponse.ContinueWith(t =>
290 var response = (HttpWebResponse)t.Result;
291 StatusCode = response.StatusCode;
292 StatusDescription = response.StatusDescription;
296 var getData = setStatus.ContinueWith(t =>
298 var response = t.Result;
299 return response.GetResponseStream()
300 .ReadAllBytesAsync();
303 var data = getData.Result;
304 var content=Encoding.UTF8.GetString(data);
311 <div class="pilwrap">
312 <a class="pilcrow" href="#section_3">¶</a>
314 <pre><code> var response = (HttpWebResponse)GetWebResponse(request);
319 <pre><code class='prettyprint'>
322 StatusCode = response.StatusCode;
323 StatusDescription = response.StatusDescription;
333 private static void TraceStart(string method, string actualAddress)
335 Trace.WriteLine(String.Format("[{0}] {1} {2}", method, DateTime.Now, actualAddress));
338 private string GetActualAddress(string address)
340 if (Parameters.Count == 0)
342 var addressBuilder=new StringBuilder(address);
345 foreach (var parameter in Parameters)
348 addressBuilder.AppendFormat("?{0}={1}", parameter.Key, parameter.Value);
350 addressBuilder.AppendFormat("&{0}={1}", parameter.Key, parameter.Value);
353 return addressBuilder.ToString();
356 public string DownloadStringWithRetry(Uri address,int retries=0)
359 throw new ArgumentNullException("address");
361 var actualRetries = (retries == 0) ? Retries : retries;
362 var task = Retry(() =>
364 var content = base.DownloadString(address);
366 if (StatusCode == HttpStatusCode.NoContent)
372 var result = task.Result;
382 <div class="pilwrap">
383 <a class="pilcrow" href="#section_4">¶</a>
386 / Copies headers from another RestClient
388 / <param name="source">The RestClient from which the headers are copied</param></p>
392 <pre><code class='prettyprint'> public void CopyHeaders(RestClient source)
394 Contract.Requires(source != null, "source can't be null");
396 throw new ArgumentNullException("source", "source can't be null");
397 CopyHeaders(source.Headers,Headers);
405 <div class="pilwrap">
406 <a class="pilcrow" href="#section_5">¶</a>
409 / Copies headers from one header collection to another
411 / <param name="source">The source collection from which the headers are copied</param>
412 / <param name="target">The target collection to which the headers are copied</param></p>
416 <pre><code class='prettyprint'> public static void CopyHeaders(WebHeaderCollection source,WebHeaderCollection target)
418 Contract.Requires(source != null, "source can't be null");
419 Contract.Requires(target != null, "target can't be null");
421 throw new ArgumentNullException("source", "source can't be null");
423 throw new ArgumentNullException("target", "target can't be null");
424 for (int i = 0; i < source.Count; i++)
426 target.Add(source.GetKey(i), source[i]);
430 public void AssertStatusOK(string message)
432 if (StatusCode >= HttpStatusCode.BadRequest)
433 throw new WebException(String.Format("{0} with code {1} - {2}", message, StatusCode, StatusDescription));
437 private Task<T> Retry<T>(Func<T> original, int retryCount, TaskCompletionSource<T> tcs = null)
440 tcs = new TaskCompletionSource<T>();
441 Task.Factory.StartNew(original).ContinueWith(_original =>
443 if (!_original.IsFaulted)
444 tcs.SetFromTask(_original);
447 var e = _original.Exception.InnerException;
448 var we = (e as WebException);
453 var statusCode = GetStatusCode(we);
460 <div class="pilwrap">
461 <a class="pilcrow" href="#section_6">¶</a>
463 <p>Return null for 404</p>
467 <pre><code class='prettyprint'> if (statusCode == HttpStatusCode.NotFound)
468 tcs.SetResult(default(T));
474 <div class="pilwrap">
475 <a class="pilcrow" href="#section_7">¶</a>
477 <p>Retry for timeouts and service unavailable</p>
481 <pre><code class='prettyprint'> else if (we.Status == WebExceptionStatus.Timeout ||
482 (we.Status == WebExceptionStatus.ProtocolError && statusCode == HttpStatusCode.ServiceUnavailable))
487 Trace.TraceError("[ERROR] Timed out too many times. {0}\n", e);
488 tcs.SetException(new RetryException("Timed out too many times.", e));
493 "[RETRY] Timed out after {0} ms. Will retry {1} more times\n{2}", Timeout,
495 Retry(original, retryCount - 1, tcs);
506 private HttpStatusCode GetStatusCode(WebException we)
508 var statusCode = HttpStatusCode.RequestTimeout;
509 if (we.Response != null)
511 statusCode = ((HttpWebResponse) we.Response).StatusCode;
512 this.StatusCode = statusCode;
518 public class RetryException:Exception
520 public RetryException()
526 public RetryException(string message)
532 public RetryException(string message,Exception innerException)
533 :base(message,innerException)
538 public RetryException(SerializationInfo info,StreamingContext context)