Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / Hammock / RestClient.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 #if NET40
5 using System.Dynamic;
6 #endif
7 using System.IO;
8 using System.Linq;
9 using System.Net;
10 using System.Reflection;
11 using System.Text;
12 using System.Threading;
13 using Hammock.Authentication;
14 using Hammock.Caching;
15 using Hammock.Extensions;
16 using Hammock.Retries;
17 using Hammock.Serialization;
18 using Hammock.Tasks;
19 using Hammock.Web;
20 using Hammock.Streaming;
21 using Hammock.Web.Mocks;
22
23 #if SILVERLIGHT
24 using Hammock.Silverlight.Compat;
25 #endif
26
27 namespace Hammock
28 {
29 #if !Silverlight
30     [Serializable]
31 #endif
32     public class RestClient : RestBase, IRestClient
33     {
34         private const string MockContentType = "mockContentType";
35         private const string MockScheme = "mockScheme";
36         private const string MockProtocol = "mock";
37         private const string MockStatusDescription = "mockStatusDescription";
38         private const string MockContent = "mockContent";
39         private const string MockHttpMethod = "mockHttpMethod";
40         private const string EndStreamingContent = "END STREAMING";
41
42         public virtual string Authority { get; set; }
43         
44         public virtual event EventHandler<FileProgressEventArgs> FileProgress;
45         public virtual void OnFileProgress(FileProgressEventArgs args)
46         {
47             var handler = FileProgress;
48             if (handler != null)
49             {
50                 handler(this, args);
51             }
52         }
53
54         public virtual event EventHandler<RetryEventArgs> BeforeRetry;
55         public virtual void OnBeforeRetry(RetryEventArgs args)
56         {
57             var handler = BeforeRetry;
58             if (handler != null)
59             {
60                 handler(this, args);
61             }
62         }
63
64 #if SILVERLIGHT
65         public virtual bool HasElevatedPermissions { get; set; }
66         /// <summary>
67         /// Used to set the name of the "Accept" header used by your Silverlight proxy.
68         /// </summary>
69         public virtual string SilverlightAcceptEncodingHeader { get; set;}
70         
71         /// <summary>
72         /// Used to set the name of the "User-Agent" header used by your Silverlight proxy.
73         /// </summary>
74         public virtual string SilverlightUserAgentHeader { get; set;}
75 #endif
76
77 #if !Silverlight
78         private bool _firstTry = true;
79 #endif
80         private int _remainingRetries;
81
82         private readonly object _timedTasksLock = new object();
83         private readonly object _streamingLock = new object();
84         private WebQuery _streamQuery;
85
86         private readonly Dictionary<RestRequest, TimedTask> _tasks = new Dictionary<RestRequest, TimedTask>();
87
88 #if !Silverlight
89
90 #if NET40
91         public RestResponse<dynamic> RequestDynamic(RestRequest request)
92         {
93             var query = RequestImpl(request);
94
95             dynamic response = BuildResponseFromResultDynamic(request, query);
96
97             return response;
98         }
99 #endif
100
101         public virtual RestResponse Request(RestRequest request)
102         {
103             var query = RequestImpl(request);
104
105             return BuildResponseFromResult(request, query);
106         }
107
108         public virtual RestResponse<T> Request<T>(RestRequest request)
109         {
110             var query = RequestImpl(request);
111
112             return BuildResponseFromResult<T>(request, query);
113         }
114
115         public RestResponse Request()
116         {
117             var query = RequestImpl(null);
118
119             return BuildResponseFromResult(null, query);
120         }
121
122         public RestResponse<T> Request<T>()
123         {
124             var query = RequestImpl(null);
125
126             return BuildResponseFromResult<T>(null, query);
127         }
128
129         private static bool _mockFactoryInitialized;
130
131         private WebQuery RequestImpl(RestRequest request)
132         {
133             Uri uri;
134             WebQuery query = null;
135             request = request ?? new RestRequest();
136
137             var retryPolicy = GetRetryPolicy(request);
138             if (_firstTry)
139             {
140                 _remainingRetries = (retryPolicy != null ? retryPolicy.RetryCount : 0) + 1;
141                 _firstTry = false;
142             }
143
144             while (_remainingRetries > 0)
145             {
146                 request = PrepareRequest(request, out uri, out query);
147
148                 var url = uri.ToString();
149
150                 if (RequestExpectsMock(request))
151                 {
152                     if (!_mockFactoryInitialized)
153                     {
154                         WebRequest.RegisterPrefix(MockProtocol, new MockWebRequestFactory());
155                         _mockFactoryInitialized = true;
156                     }
157
158                     url = BuildMockRequestUrl(request, query, url);
159                 }
160
161                 UpdateRetryState(request);
162
163                 WebException exception;
164                 if (!RequestWithCache(request, query, url, out exception) &&
165                     !RequestMultiPart(request, query, url, out exception))
166                 {
167                     query.Request(url, out exception);
168                 }
169
170                 query.Result.Exception = exception;
171                 var current = query.Result;
172
173                 if (retryPolicy != null)
174                 {
175                     var retry = _remainingRetries > 0 && ShouldRetry(retryPolicy, exception, current);
176
177                     if (retry)
178                     {
179                         _remainingRetries--;
180                         if (_remainingRetries > 0)
181                         {
182                             query.Result = new WebQueryResult { TimesTried = GetIterationCount(request) };
183
184                             OnBeforeRetry(new RetryEventArgs { Client = this, Request = request });
185                         }
186                     }
187                     else
188                     {
189                         _remainingRetries = 0;
190                     }
191                 }
192                 else
193                 {
194                     _remainingRetries = 0;
195                 }
196             }
197
198             _firstTry = _remainingRetries == 0;
199             return query;
200         }
201
202         private RestRequest PrepareRequest(RestRequest request, out Uri uri, out WebQuery query)
203         {
204             request = request ?? new RestRequest();
205             uri = request.BuildEndpoint(this);
206             query = GetQueryFor(request, uri);
207             SetQueryMeta(request, query);
208             return request;
209         }
210
211         private bool RequestMultiPart(RestBase request, WebQuery query, string url, out WebException exception)
212         {
213             var parameters = GetPostParameters(request);
214             if (parameters == null || parameters.Count() == 0)
215             {
216                 exception = null;
217                 return false;
218             }
219
220             // [DC]: Default to POST if no method provided
221             query.Method = query.Method != WebMethod.Post && query.Method != WebMethod.Put ? WebMethod.Post : query.Method;
222             query.Request(url, parameters, out exception);
223             return true;
224         }
225
226         private bool RequestWithCache(RestBase request, WebQuery query, string url, out WebException exception)
227         {
228             var cache = GetCache(request);
229             if (cache == null)
230             {
231                 exception = null;
232                 return false;
233             }
234
235             var options = GetCacheOptions(request);
236             if (options == null)
237             {
238                 exception = null;
239                 return false;
240             }
241
242             // [DC]: This is currently prefixed to the full URL
243             var function = GetCacheKeyFunction(request);
244             var key = function != null ? function.Invoke() : "";
245
246             switch (options.Mode)
247             {
248                 case CacheMode.NoExpiration:
249                     query.Request(url, key, cache, out exception);
250                     break;
251                 case CacheMode.AbsoluteExpiration:
252                     var expiry = options.Duration.FromNow();
253                     query.Request(url, key, cache, expiry, out exception);
254                     break;
255                 case CacheMode.SlidingExpiration:
256                     query.Request(url, key, cache, options.Duration, out exception);
257                     break;
258                 default:
259                     throw new NotSupportedException("Unknown CacheMode");
260             }
261
262             return true;
263         }
264 #endif
265         private void UpdateRepeatingRequestState(RestBase request)
266         {
267             var taskState = request.TaskState ?? TaskState;
268             if (taskState == null)
269             {
270                 return;
271             }
272             taskState.RepeatCount++;
273             taskState.LastRepeat = DateTime.Now;
274         }
275
276         private void UpdateRetryState(RestBase request)
277         {
278             var retryState = request.RetryState ?? RetryState;
279             if (retryState == null)
280             {
281                 return;
282             }
283
284             retryState.RepeatCount++;
285             retryState.LastRepeat = DateTime.Now;
286         }
287
288         private static bool ShouldRetry(RetryPolicy retryPolicy,
289                                         Exception exception,
290                                         WebQueryResult current)
291         {
292             var retry = false;
293             foreach (var condition in retryPolicy.RetryConditions.OfType<RetryErrorCondition>())
294             {
295                 if (exception == null)
296                 {
297                     continue;
298                 }
299                 retry |= condition.RetryIf(exception);
300             }
301
302             foreach (var condition in retryPolicy.RetryConditions.OfType<RetryResultCondition>())
303             {
304                 if (current == null)
305                 {
306                     continue;
307                 }
308                 retry |= condition.RetryIf(current);
309             }
310
311             foreach (var condition in retryPolicy.RetryConditions.OfType<IRetryCustomCondition>())
312             {
313                 var innerType = condition.GetDeclaredTypeForGeneric(typeof(IRetryCondition<>));
314                 if (innerType == null)
315                 {
316                     continue;
317                 }
318
319                 /*
320                 var retryType = typeof(RetryCustomCondition<>).MakeGenericType(innerType);
321                 if (retryType == null)
322                 {
323                     continue;
324                 }
325                 */
326
327                 var func = condition.GetValue("ConditionFunction") as MulticastDelegate;
328                 if (func == null)
329                 {
330                     continue;
331                 }
332
333                 // Call the function to find the retry evaluator
334 #if !Smartphone && !NETCF
335                 var t = func.DynamicInvoke(null);
336 #else
337                 var del = func.GetInvocationList().FirstOrDefault();
338                 var t = del.Method.Invoke(func, null);
339 #endif
340
341                 // Invoke the retry predicate and pass the evaluator
342                 var p = condition.GetValue("RetryIf");
343                 var r = p.GetType().InvokeMember("Invoke",
344                     BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
345                     null, p, new[] { t });
346
347                 retry |= (bool)r;
348             }
349
350             return retry;
351         }
352
353         private string BuildMockRequestUrl(RestRequest request,
354                                            WebQuery query,
355                                            string url)
356         {
357             if (url.Contains("https"))
358             {
359                 url = url.Replace("https", MockProtocol);
360
361                 query.Parameters.Add(MockScheme, "https");
362             }
363             if (url.Contains("http"))
364             {
365                 url = url.Replace("http", MockProtocol);
366                 query.Parameters.Add(MockScheme, "http");
367             }
368
369             if (request.ExpectStatusCode.HasValue)
370             {
371                 query.Parameters.Add("mockStatusCode", ((int)request.ExpectStatusCode.Value).ToString());
372                 if (request.ExpectStatusDescription.IsNullOrBlank())
373                 {
374                     query.Parameters.Add(MockStatusDescription, request.ExpectStatusCode.ToString());
375                 }
376             }
377             if (!request.ExpectStatusDescription.IsNullOrBlank())
378             {
379                 query.Parameters.Add(MockStatusDescription, request.ExpectStatusDescription);
380             }
381
382             query.Parameters.Add(
383                 MockHttpMethod, request.Method.ToString().ToUpper()
384                 );
385
386             var expectEntity = SerializeExpectEntity(request);
387             if (expectEntity != null)
388             {
389                 query.Parameters.Add(MockContent, expectEntity.Content);
390                 query.Parameters.Add(MockContentType, expectEntity.ContentType);
391                 query.HasEntity = true; // Used with POSTs
392             }
393             else
394             {
395                 if (!request.ExpectContent.IsNullOrBlank())
396                 {
397                     query.Parameters.Add(MockContent, request.ExpectContent);
398                     query.Parameters.Add(MockContentType,
399                                          !request.ExpectContentType.IsNullOrBlank()
400                                              ? request.ExpectContentType
401                                              : "text/html"
402                         );
403                 }
404                 else
405                 {
406                     if (!request.ExpectContentType.IsNullOrBlank())
407                     {
408                         query.Parameters.Add(
409                             MockContentType, request.ExpectContentType
410                             );
411                     }
412                 }
413             }
414
415             if (request.ExpectHeaders.Count > 0)
416             {
417                 var names = new StringBuilder();
418                 var values = new StringBuilder();
419                 var count = 0;
420                 foreach (var key in request.ExpectHeaders.AllKeys)
421                 {
422                     names.Append(key);
423                     values.Append(request.ExpectHeaders[key].Value);
424                     count++;
425                     if (count < request.ExpectHeaders.Count)
426                     {
427                         names.Append(",");
428                         values.Append(",");
429                     }
430                 }
431
432                 query.Parameters.Add("mockHeaderNames", names.ToString());
433                 query.Parameters.Add("mockHeaderValues", values.ToString());
434             }
435
436             return url;
437         }
438
439         private static bool RequestExpectsMock(RestRequest request)
440         {
441             return request.ExpectEntity != null ||
442                    request.ExpectHeaders.Count > 0 ||
443                    request.ExpectStatusCode.HasValue ||
444                    !request.ExpectContent.IsNullOrBlank() ||
445                    !request.ExpectContentType.IsNullOrBlank() ||
446                    !request.ExpectStatusDescription.IsNullOrBlank();
447         }
448
449         private ICache GetCache(RestBase request)
450         {
451             return request.Cache ?? Cache;
452         }
453
454         private IEnumerable<HttpPostParameter> GetPostParameters(RestBase request)
455         {
456             if (request.PostParameters != null)
457             {
458                 foreach (var parameter in request.PostParameters)
459                 {
460                     yield return parameter;
461                 }
462             }
463
464             if (PostParameters == null)
465             {
466                 yield break;
467             }
468
469             foreach (var parameter in PostParameters)
470             {
471                 yield return parameter;
472             }
473         }
474
475         private CacheOptions GetCacheOptions(RestBase request)
476         {
477             return request.CacheOptions ?? CacheOptions;
478         }
479
480         private Func<string> GetCacheKeyFunction(RestBase request)
481         {
482             return request.CacheKeyFunction ?? CacheKeyFunction;
483         }
484
485         private string GetProxy(RestBase request)
486         {
487             return request.Proxy ?? Proxy;
488         }
489
490         private string GetUserAgent(RestBase request)
491         {
492             var userAgent = request.UserAgent.IsNullOrBlank()
493                                 ? UserAgent
494                                 : request.UserAgent;
495             return userAgent;
496         }
497
498         private ISerializer GetSerializer(RestBase request)
499         {
500             return request.Serializer ?? Serializer;
501         }
502
503         private IWebCredentials GetWebCredentials(RestBase request)
504         {
505             var credentials = request.Credentials ?? Credentials;
506             return credentials;
507         }
508
509         private IWebQueryInfo GetInfo(RestBase request)
510         {
511             var info = request.Info ?? Info;
512             return info;
513         }
514
515         private bool GetTraceEnabled(RestBase request)
516         {
517             var info = request.TraceEnabled || TraceEnabled;
518             return info;
519         }
520
521         private TimeSpan? GetTimeout(RestBase request)
522         {
523             return request.Timeout ?? Timeout;
524         }
525
526         private DecompressionMethods? GetDecompressionMethods(RestBase request)
527         {
528             return request.DecompressionMethods ?? DecompressionMethods;
529         }
530
531         private WebMethod GetWebMethod(RestBase request)
532         {
533             var method = !request.Method.HasValue
534                              ? !Method.HasValue
535                                    ? WebMethod.Get
536                                    : Method.Value
537                              : request.Method.Value;
538
539             return method;
540         }
541
542         private byte[] GetPostContent(RestBase request)
543         {
544             var content = request.PostContent ?? PostContent;
545             return content;
546         }
547
548         private RetryPolicy GetRetryPolicy(RestBase request)
549         {
550             var policy = request.RetryPolicy ?? RetryPolicy;
551             return policy;
552         }
553
554         private Encoding GetEncoding(RestBase request)
555         {
556             var encoding = request.Encoding ?? Encoding;
557             return encoding;
558         }
559
560 #if !SILVERLIGHT
561         private bool GetFollowRedirects(RestBase request)
562         {
563             var redirects = request.FollowRedirects ?? FollowRedirects ?? false;
564             return redirects;
565         }
566 #endif
567
568         private TaskOptions GetTaskOptions(RestBase request)
569         {
570             var options = request.TaskOptions ?? TaskOptions;
571             return options;
572         }
573
574         private StreamOptions GetStreamOptions(RestBase request)
575         {
576             var options = request.StreamOptions ?? StreamOptions;
577             return options;
578         }
579
580         private object GetTag(RestBase request)
581         {
582             var tag = request.Tag ?? Tag;
583             return tag;
584         }
585
586 #if !WindowsPhone
587         public virtual IAsyncResult BeginRequest(RestRequest request, RestCallback callback, object userState)
588         {
589             return BeginRequestImpl(request, callback, null, null, false /* isInternal */, userState);
590         }
591
592         public virtual IAsyncResult BeginRequest<T>(RestRequest request, RestCallback<T> callback, object userState)
593         {
594             return BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
595         }
596
597         public IAsyncResult BeginRequest()
598         {
599             return BeginRequest(null /* request */, null /* callback */);
600         }
601
602         public IAsyncResult BeginRequest<T>()
603         {
604             return BeginRequest(null /* request */, null /* callback */);
605         }
606
607 #if NET40
608         public IAsyncResult BeginRequestDynamic()
609         {
610             return BeginRequestDynamic(null /* request */, null /* callback */);
611         }
612 #endif
613
614         public virtual IAsyncResult BeginRequest(RestRequest request, RestCallback callback)
615         {
616             return BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
617         }
618
619         public virtual IAsyncResult BeginRequest<T>(RestRequest request, RestCallback<T> callback)
620         {
621             return BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
622         }
623         
624 #if NET40
625         public virtual IAsyncResult BeginRequestDynamic(RestRequest request, RestCallback<dynamic> callback)
626         {
627             return BeginRequestImplDynamic(request, callback, null, null, false /* isInternal */, null);
628         }
629         
630         public virtual IAsyncResult BeginRequestDynamic(RestRequest request, RestCallback<dynamic> callback, object userState)
631         {
632             return BeginRequestImplDynamic(request, callback, null, null, false /* isInternal */, userState);
633         }
634 #endif
635
636         public virtual IAsyncResult BeginRequest(RestCallback callback)
637         {
638             return BeginRequestImpl(null, callback, null, null, false /* isInternal */, null);
639         }
640
641         public virtual IAsyncResult BeginRequest(RestRequest request)
642         {
643             return BeginRequest(request, null, null);
644         }
645
646         public IAsyncResult BeginRequest(RestRequest request, object userState)
647         {
648             return BeginRequest(request, null, userState);
649         }
650
651         public virtual IAsyncResult BeginRequest<T>(RestRequest request)
652         {
653             return BeginRequest<T>(request, null, null);
654         }
655
656         public IAsyncResult BeginRequest<T>(RestRequest request, object userState)
657         {
658             return BeginRequest<T>(request, null, userState);
659         }
660
661         public virtual IAsyncResult BeginRequest<T>(RestCallback<T> callback)
662         {
663             return BeginRequest(null, callback, null);
664         }
665 #else
666         public virtual void BeginRequest(RestRequest request, RestCallback callback, object userState)
667         {
668             BeginRequestImpl(request, callback, null, null, false /* isInternal */, userState);
669         }
670
671         public virtual void BeginRequest<T>(RestRequest request, RestCallback<T> callback, object userState)
672         {
673             BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
674         }
675
676         public virtual void BeginRequest()
677         {
678             BeginRequest(null /* request */, null /* callback */);
679         }
680
681         public virtual void BeginRequest<T>()
682         {
683             BeginRequest(null /* request */, null /* callback */);
684         }
685
686         public virtual void BeginRequest(RestRequest request, RestCallback callback)
687         {
688             BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
689         }
690
691         public virtual void BeginRequest<T>(RestRequest request, RestCallback<T> callback)
692         {
693             BeginRequestImpl(request, callback, null, null, false /* isInternal */, null);
694         }
695
696         public virtual void BeginRequest(RestCallback callback)
697         {
698             BeginRequestImpl(null, callback, null, null, false /* isInternal */, null);
699         }
700
701         public virtual void BeginRequest(RestRequest request)
702         {
703             BeginRequest(request, null, null);
704         }
705
706         public void BeginRequest(RestRequest request, object userState)
707         {
708             BeginRequest(request, null, userState);
709         }
710
711         public virtual void BeginRequest<T>(RestRequest request)
712         {
713             BeginRequest<T>(request, null, null);
714         }
715
716         public void BeginRequest<T>(RestRequest request, object userState)
717         {
718             BeginRequest<T>(request, null, userState);
719         }
720
721         public virtual void BeginRequest<T>(RestCallback<T> callback)
722         {
723             BeginRequest(null, callback, null);
724         }
725 #endif
726
727 #if !WindowsPhone
728         public virtual RestResponse EndRequest(IAsyncResult result)
729         {
730 #if !Mono && !NETCF
731             var webResult = EndRequestImpl(result);
732 #else
733           var webResult = EndRequestImpl(result, null);
734 #endif
735             return webResult.AsyncState as RestResponse;
736         }
737
738         public virtual RestResponse EndRequest(IAsyncResult result, TimeSpan timeout)
739         {
740             var webResult = EndRequestImpl(result, timeout);
741             return webResult.AsyncState as RestResponse;
742         }
743
744         public virtual RestResponse<T> EndRequest<T>(IAsyncResult result)
745         {
746 #if !Mono && !NETCF
747             var webResult = EndRequestImpl<T>(result);
748 #else
749           var webResult = EndRequestImpl<T>(result, null);
750 #endif
751             return webResult.AsyncState as RestResponse<T>;
752         }
753
754 #if NET40
755         public virtual RestResponse<dynamic> EndRequestDynamic(IAsyncResult result)
756         {
757 #if !Mono
758             var webResult = EndRequestImplDynamic(result);
759 #else
760                         var webResult = EndRequestImplDynamic<T>(result, null);
761 #endif
762             return webResult.AsyncState as RestResponse<dynamic>;
763         }
764 #endif
765
766         public virtual RestResponse<T> EndRequest<T>(IAsyncResult result, TimeSpan timeout)
767         {
768             var webResult = EndRequestImpl<T>(result, timeout);
769             return webResult.AsyncState as RestResponse<T>;
770         }
771
772 #if !Mono && !NETCF
773         private WebQueryAsyncResult EndRequestImpl(IAsyncResult result, TimeSpan? timeout = null)
774                 {
775 #else
776                 private WebQueryAsyncResult EndRequestImpl(IAsyncResult result, TimeSpan? timeout)
777                 {                                       
778 #endif
779             var webResult = result as WebQueryAsyncResult;
780             if (webResult == null)
781             {
782                 throw new InvalidOperationException("The IAsyncResult provided was not for this operation.");
783             }
784
785             var tag = (Triplet<RestRequest, RestCallback, object>)webResult.Tag;
786
787             if (RequestExpectsMock(tag.First))
788             {
789                 // [DC]: Mock results come via InnerResult
790                 webResult = (WebQueryAsyncResult)webResult.InnerResult;
791             }
792
793             if (webResult.CompletedSynchronously)
794             {
795                 var query = webResult.AsyncState as WebQuery;
796                 if (query != null)
797                 {
798                     // [DC]: From cache
799                     CompleteWithQuery(query, tag.First, tag.Second, webResult);
800                 }
801                 else
802                 {
803                     // [DC]: From mocks
804                     webResult = CompleteWithMockWebResponse(result, webResult, tag);
805                 }
806             }
807
808             if (!webResult.IsCompleted)
809             {
810                 if(timeout.HasValue)
811                 {
812 #if NETCF
813                   var millisecondsTimeout = Convert.ToInt32(timeout.Value.TotalMilliseconds);
814                   webResult.AsyncWaitHandle.WaitOne(millisecondsTimeout, false);
815 #else
816                     webResult.AsyncWaitHandle.WaitOne(timeout.Value);
817 #endif
818                 }
819                 else
820                 {
821                     webResult.AsyncWaitHandle.WaitOne();
822                 }
823             }
824             return webResult;
825     }
826
827 #if !Mono && !NETCF
828         private WebQueryAsyncResult EndRequestImpl<T>(IAsyncResult result, TimeSpan? timeout = null)
829 #else
830     private WebQueryAsyncResult EndRequestImpl<T>(IAsyncResult result, TimeSpan? timeout)
831 #endif
832         {
833             var webResult = result as WebQueryAsyncResult;
834             if (webResult == null)
835             {
836                 throw new InvalidOperationException("The IAsyncResult provided was not for this operation.");
837             }
838
839             var tag = (Triplet<RestRequest, RestCallback<T>, object>)webResult.Tag;
840
841             if (RequestExpectsMock(tag.First))
842             {
843                 // [DC]: Mock results come via InnerResult
844                 webResult = (WebQueryAsyncResult)webResult.InnerResult;
845             }
846
847             if (webResult.CompletedSynchronously)
848             {
849                 var query = webResult.AsyncState as WebQuery;
850                 if (query != null)
851                 {
852                     // [DC]: From cache
853                     CompleteWithQuery(query, tag.First, tag.Second, webResult);
854                 }
855                 else
856                 {
857                     // [DC]: From mocks
858                     webResult = CompleteWithMockWebResponse(result, webResult, tag);
859                 }
860             }
861
862             if (!webResult.IsCompleted)
863             {
864                 if(timeout.HasValue)
865                 {
866 #if NETCF
867                   var millisecondsTimeout = Convert.ToInt32(timeout.Value.TotalMilliseconds);
868                   webResult.AsyncWaitHandle.WaitOne(millisecondsTimeout, false);
869 #else
870                     webResult.AsyncWaitHandle.WaitOne(timeout.Value);
871 #endif
872                 }
873                 else
874                 {
875                     webResult.AsyncWaitHandle.WaitOne();
876                 }
877             }
878             return webResult;
879         }
880 #endif
881
882 #if NET40
883 #if !Mono 
884         private WebQueryAsyncResult EndRequestImplDynamic(IAsyncResult result, TimeSpan? timeout = null)
885 #else
886                 private WebQueryAsyncResult EndRequestImplDynamic(IAsyncResult result, TimeSpan? timeout)
887 #endif
888         {
889             var webResult = result as WebQueryAsyncResult;
890             if (webResult == null)
891             {
892                 throw new InvalidOperationException("The IAsyncResult provided was not for this operation.");
893             }
894
895             var tag = (Triplet<RestRequest, RestCallback<dynamic>, object>) webResult.Tag;
896
897             if (RequestExpectsMock(tag.First))
898             {
899                 // [DC]: Mock results come via InnerResult
900                 webResult = (WebQueryAsyncResult) webResult.InnerResult;
901             }
902
903             if (webResult.CompletedSynchronously)
904             {
905                 var query = webResult.AsyncState as WebQuery;
906                 if (query != null)
907                 {
908                     // [DC]: From cache
909                     CompleteWithQueryDynamic(query, tag.First, tag.Second, webResult);
910                 }
911                 else
912                 {
913                     // [DC]: From mocks
914                     webResult = CompleteWithMockWebResponse(result, webResult, tag);
915                 }
916             }
917
918             if (!webResult.IsCompleted)
919             {
920                 if(timeout.HasValue)
921                 {
922                     webResult.AsyncWaitHandle.WaitOne(timeout.Value);
923                 }
924                 else
925                 {
926                     webResult.AsyncWaitHandle.WaitOne();
927                 }
928             }
929             return webResult;
930         }
931 #endif
932
933         private WebQueryAsyncResult CompleteWithMockWebResponse<T>(
934             IAsyncResult result,
935             IAsyncResult webResult,
936             Triplet<RestRequest, RestCallback<T>, object> tag)
937         {
938             var webResponse = (WebResponse)webResult.AsyncState;
939             var restRequest = tag.First;
940             var userState = tag.Third;
941
942             var m = new MemoryStream();
943             using (var stream = webResponse.GetResponseStream())
944             {
945                 if (stream != null)
946                 {
947                     using (var reader = new StreamReader(stream))
948                     {
949                         while (reader.Peek() >= 0)
950                         {
951                             m.WriteByte((byte) reader.Read());
952                         }
953                     }
954                 }
955             }
956             m.Position = 0;
957             
958             var restResponse = new RestResponse<T>
959                                    {
960                                        ContentStream = m,
961                                        ContentType = webResponse.ContentType,
962                                        ContentLength = webResponse.ContentLength,
963                                        StatusCode = restRequest.ExpectStatusCode.HasValue
964                                                         ? restRequest.ExpectStatusCode.Value
965                                                         : 0,
966                                        StatusDescription = restRequest.ExpectStatusDescription,
967                                        ResponseUri = webResponse.ResponseUri,
968                                        IsMock = true
969                                    };
970
971             foreach (var key in webResponse.Headers.AllKeys)
972             {
973                 restResponse.Headers.Add(key, webResponse.Headers[key]);
974             }
975
976             var deserializer = restRequest.Deserializer ?? Deserializer;
977             if (deserializer != null && !restResponse.Content.IsNullOrBlank())
978             {
979                 restResponse.ContentEntity = deserializer.Deserialize<T>(restResponse);
980             }
981
982             TraceResponseWithMock(restResponse);
983
984             var parentResult = (WebQueryAsyncResult)result;
985             parentResult.AsyncState = restResponse;
986             parentResult.IsCompleted = true;
987
988             var callback = tag.Second;
989             if (callback != null)
990             {
991                 callback.Invoke(restRequest, restResponse, userState);
992             }
993             parentResult.Signal();
994             return parentResult;
995         }
996
997         private WebQueryAsyncResult CompleteWithMockWebResponse(
998             IAsyncResult result,
999             IAsyncResult webResult,
1000             Triplet<RestRequest, RestCallback, object> tag)
1001         {
1002             var webResponse = (WebResponse)webResult.AsyncState;
1003             var restRequest = tag.First;
1004             var userState = tag.Third;
1005
1006             var m = new MemoryStream();
1007             using (var stream = webResponse.GetResponseStream())
1008             {
1009                 if (stream != null)
1010                 {
1011                     using (var reader = new StreamReader(stream))
1012                     {
1013                         while (reader.Peek() >= 0)
1014                         {
1015                             m.WriteByte((byte) reader.Read());
1016                         }
1017                     }
1018                 }
1019             }
1020             m.Position = 0;
1021
1022             var restResponse = new RestResponse
1023                                    {
1024                                        ContentStream = m,
1025                                        ContentType = webResponse.ContentType,
1026                                        ContentLength = webResponse.ContentLength,
1027                                        StatusCode = restRequest.ExpectStatusCode.HasValue
1028                                                         ? restRequest.ExpectStatusCode.Value
1029                                                         : 0,
1030                                        StatusDescription = restRequest.ExpectStatusDescription,
1031                                        ResponseUri = webResponse.ResponseUri,
1032                                        IsMock = true
1033                                    };
1034
1035             foreach (var key in webResponse.Headers.AllKeys)
1036             {
1037                 restResponse.Headers.Add(key, webResponse.Headers[key]);
1038             }
1039
1040             var deserializer = restRequest.Deserializer ?? Deserializer;
1041             if (deserializer != null && !restResponse.Content.IsNullOrBlank() && restRequest.ResponseEntityType != null)
1042             {
1043                 restResponse.ContentEntity = deserializer.Deserialize(restResponse, restRequest.ResponseEntityType);
1044             }
1045
1046             TraceResponseWithMock(restResponse);
1047
1048             var parentResult = (WebQueryAsyncResult)result;
1049             parentResult.AsyncState = restResponse;
1050             parentResult.IsCompleted = true;
1051
1052             var callback = tag.Second;
1053             if (callback != null)
1054             {
1055                 callback.Invoke(restRequest, restResponse, userState);
1056             }
1057             parentResult.Signal();
1058             return parentResult;
1059         }
1060
1061         private void TraceResponseWithMock(RestResponseBase restResponse)
1062         {
1063 #if TRACE
1064             if(!TraceEnabled)
1065             {
1066                 return;
1067             }
1068
1069             Trace.WriteLine(string.Concat("RESPONSE: ", restResponse.StatusCode, " ", restResponse.StatusDescription));
1070             Trace.WriteLineIf(restResponse.Headers.AllKeys.Count() > 0, "HEADERS:");
1071             foreach (var trace in restResponse.Headers.AllKeys.Select(key => string.Concat("\t", key, ": ", restResponse.Headers[key])))
1072             {
1073                 Trace.WriteLine(trace);
1074             }
1075             Trace.WriteLine(string.Concat("\r\n", restResponse.Content));
1076 #endif
1077         }
1078
1079 #if !WindowsPhone
1080         // TODO BeginRequestImpl and BeginRequestImpl<T> have too much duplication
1081         private IAsyncResult BeginRequestImpl(RestRequest request,
1082                                           RestCallback callback,
1083                                           WebQuery query,
1084                                           string url,
1085                                           bool isInternal,
1086                                           object userState)
1087         {
1088             request = request ?? new RestRequest();
1089             if (!isInternal)
1090             {
1091                 // [DC]: Recursive call possible, only do this once
1092                 var uri = request.BuildEndpoint(this);
1093                 query = GetQueryFor(request, uri);
1094                 SetQueryMeta(request, query);
1095                 url = uri.ToString();
1096             }
1097
1098             if (RequestExpectsMock(request))
1099             {
1100                 url = BuildMockRequestUrl(request, query, url);
1101             }
1102
1103             var retryPolicy = GetRetryPolicy(request);
1104             bool inRetryScope = false;
1105             if (!isInternal && retryPolicy != null)
1106             {
1107                 _remainingRetries = retryPolicy.RetryCount;
1108                 inRetryScope = true;
1109             }
1110
1111             Func<WebQueryAsyncResult> beginRequest;
1112             WebQueryAsyncResult asyncResult;
1113
1114             var streamOptions = GetStreamOptions(request);
1115             if (streamOptions != null)
1116             {
1117 #if !SILVERLIGHT
1118                 query.KeepAlive = true;
1119 #endif
1120                 var duration = streamOptions.Duration.HasValue
1121                                    ? streamOptions.Duration.Value
1122                                    : TimeSpan.Zero;
1123
1124                 var resultCount = streamOptions.ResultsPerCallback.HasValue
1125                                       ? streamOptions.ResultsPerCallback.Value
1126                                       : 10;
1127
1128                 beginRequest = () => BeginRequestStreamFunction(
1129                    request, query, url, callback, duration, resultCount, userState
1130                    );
1131
1132                 asyncResult = beginRequest.Invoke();
1133             }
1134             else
1135             {
1136                 beginRequest
1137                = () => BeginRequestFunction(isInternal,
1138                        request,
1139                        query,
1140                        url,
1141                        callback,
1142                        userState);
1143
1144                 asyncResult = beginRequest.Invoke();
1145             }
1146
1147             if (isInternal || (request.TaskOptions == null || request.TaskOptions.RepeatInterval.TotalMilliseconds == 0))
1148             {
1149                 if (IsFirstIteration && request.IsFirstIteration)
1150                 {
1151                     query.QueryResponse += (sender, args) =>
1152                                                {
1153                                                    var current = query.Result;
1154
1155                                                    if (retryPolicy != null)
1156                                                    {
1157                                                        // [DC]: Query should already have exception applied
1158                                                        var exception = query.Result.Exception;
1159                                                        var retry = inRetryScope &&
1160                                                                    _remainingRetries > 0 &&
1161                                                                    ShouldRetry(retryPolicy, exception, current);
1162
1163                                                        if (retry)
1164                                                        {
1165                                                            UpdateRetryState(request);
1166                                                            BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
1167                                                            Interlocked.Decrement(ref _remainingRetries);
1168                                                            if(_remainingRetries > 0)
1169                                                            {
1170                                                                OnBeforeRetry(new RetryEventArgs { Client = this, Request = request });
1171                                                            }
1172                                                        }
1173                                                        else if (inRetryScope)
1174                                                        {
1175                                                            _remainingRetries = 0;
1176                                                        }
1177                                                        current.TimesTried = GetIterationCount(request);
1178                                                    }
1179                                                    else if (inRetryScope)
1180                                                    {
1181                                                        _remainingRetries = 0;
1182                                                    }
1183
1184                                                    query.Result = current;
1185
1186                                                    if (_remainingRetries == 0)
1187                                                    {
1188                                                        CompleteWithQuery(query, request, callback, asyncResult);
1189                                                    }
1190                                                };
1191                 }
1192                 UpdateRepeatingRequestState(request);
1193             }
1194             return asyncResult;
1195         }
1196
1197
1198 #if NET40
1199         private IAsyncResult BeginRequestImplDynamic(RestRequest request,
1200                                                         RestCallback<dynamic> callback,
1201                                                         WebQuery query,
1202                                                         string url,
1203                                                         bool isInternal,
1204                                                         object userState)
1205         {
1206             request = request ?? new RestRequest();
1207             if (!isInternal)
1208             {
1209                 var uri = request.BuildEndpoint(this);
1210                 query = GetQueryFor(request, uri);
1211                 SetQueryMeta(request, query);
1212                 url = uri.ToString();
1213             }
1214
1215             if (RequestExpectsMock(request))
1216             {
1217                 url = BuildMockRequestUrl(request, query, url);
1218             }
1219
1220             var retryPolicy = GetRetryPolicy(request);
1221             bool inRetryScope = false;
1222             if (!isInternal && retryPolicy != null)
1223             {
1224                 _remainingRetries = retryPolicy.RetryCount;
1225                 inRetryScope = true;
1226             }
1227
1228             Func<WebQueryAsyncResult> beginRequest;
1229             WebQueryAsyncResult asyncResult;
1230
1231             var streamOptions = GetStreamOptions(request);
1232             if (streamOptions != null)
1233             {
1234 #if !SILVERLIGHT
1235                 query.KeepAlive = true;
1236 #endif
1237
1238                 var duration = streamOptions.Duration.HasValue
1239                                    ? streamOptions.Duration.Value
1240                                    : TimeSpan.Zero;
1241
1242                 var resultCount = streamOptions.ResultsPerCallback.HasValue
1243                                       ? streamOptions.ResultsPerCallback.Value
1244                                       : 10;
1245
1246                 beginRequest = () => BeginRequestStreamFunction(
1247                    request, query, url, callback, duration, resultCount, userState
1248                    );
1249
1250                 asyncResult = beginRequest.Invoke();
1251             }
1252             else
1253             {
1254                 beginRequest = () => BeginRequestFunction(
1255                    isInternal, request, query, url, callback, userState
1256                    );
1257
1258                 asyncResult = beginRequest.Invoke();
1259             }
1260             if (isInternal || (request.TaskOptions == null || request.TaskOptions.RepeatInterval.TotalMilliseconds == 0))
1261             {
1262                 if (IsFirstIteration && request.IsFirstIteration)
1263                 {
1264                     query.QueryResponse += (sender, args) =>
1265                     {
1266                         var current = query.Result;
1267
1268                         if (retryPolicy != null)
1269                         {
1270                             // [DC]: Query should already have exception applied
1271                             var exception = query.Result.Exception;
1272                             var retry = inRetryScope &&
1273                                         _remainingRetries > 0 &&
1274                                         ShouldRetry(retryPolicy, exception, current);
1275
1276                             if (retry)
1277                             {
1278                                 UpdateRetryState(request);
1279                                 BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
1280                                 Interlocked.Decrement(ref _remainingRetries);
1281                                 if (_remainingRetries > 0)
1282                                 {
1283                                     OnBeforeRetry(new RetryEventArgs { Client = this, Request = request });
1284                                 }
1285                             }
1286                             else if (inRetryScope)
1287                             {
1288                                 _remainingRetries = 0;
1289                             }
1290                             current.TimesTried = GetIterationCount(request);
1291                         }
1292                         else if (inRetryScope)
1293                         {
1294                             _remainingRetries = 0;
1295                         }
1296
1297                         query.Result = current;
1298
1299                         // [DC]: Callback is for a final result, not a retry
1300                         if (_remainingRetries == 0)
1301                         {
1302                             CompleteWithQueryDynamic(query, request, callback, asyncResult);
1303                         }
1304                     };
1305                 }
1306                 UpdateRepeatingRequestState(request);
1307             }
1308             return asyncResult;
1309         }
1310 #endif
1311
1312         private IAsyncResult BeginRequestImpl<T>(RestRequest request,
1313                                                  RestCallback<T> callback,
1314                                                  WebQuery query,
1315                                                  string url,
1316                                                  bool isInternal,
1317                                                  object userState)
1318         {
1319             request = request ?? new RestRequest();
1320             if (!isInternal)
1321             {
1322                 var uri = request.BuildEndpoint(this);
1323                 query = GetQueryFor(request, uri);
1324                 SetQueryMeta(request, query);
1325                 url = uri.ToString();
1326             }
1327
1328             if (RequestExpectsMock(request))
1329             {
1330                 url = BuildMockRequestUrl(request, query, url);
1331             }
1332
1333             var retryPolicy = GetRetryPolicy(request);
1334             bool inRetryScope = false;
1335             if (!isInternal && retryPolicy != null)
1336             {
1337                 _remainingRetries = retryPolicy.RetryCount;
1338                 inRetryScope = true;
1339             }
1340
1341             Func<WebQueryAsyncResult> beginRequest;
1342             WebQueryAsyncResult asyncResult;
1343
1344             var streamOptions = GetStreamOptions(request);
1345             if (streamOptions != null)
1346             {
1347 #if !SILVERLIGHT
1348                 query.KeepAlive = true;
1349 #endif
1350
1351                 var duration = streamOptions.Duration.HasValue
1352                                    ? streamOptions.Duration.Value
1353                                    : TimeSpan.Zero;
1354
1355                 var resultCount = streamOptions.ResultsPerCallback.HasValue
1356                                       ? streamOptions.ResultsPerCallback.Value
1357                                       : 10;
1358
1359                 beginRequest = () => BeginRequestStreamFunction(
1360                    request, query, url, callback, duration, resultCount, userState
1361                    );
1362
1363                 asyncResult = beginRequest.Invoke();
1364             }
1365             else
1366             {
1367                 beginRequest = () => BeginRequestFunction(
1368                    isInternal, request, query, url, callback, userState
1369                    );
1370
1371                 asyncResult = beginRequest.Invoke();
1372             }
1373             if (isInternal || (request.TaskOptions == null || request.TaskOptions.RepeatInterval.TotalMilliseconds == 0))
1374             {
1375                 if (IsFirstIteration && request.IsFirstIteration)
1376                 {
1377                     query.QueryResponse += (sender, args) =>
1378                        {
1379                            var current = query.Result;
1380
1381                            if (retryPolicy != null)
1382                            {
1383                                // [DC]: Query should already have exception applied
1384                                var exception = query.Result.Exception;
1385                                var retry = inRetryScope &&
1386                                            _remainingRetries > 0 &&
1387                                            ShouldRetry(retryPolicy, exception, current);
1388
1389                                if (retry)
1390                                {
1391                                    UpdateRetryState(request);
1392                                    BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
1393                                    Interlocked.Decrement(ref _remainingRetries);
1394                                    if(_remainingRetries > 0)
1395                                    {
1396                                        OnBeforeRetry(new RetryEventArgs { Client = this, Request = request });
1397                                    }
1398                                }
1399                                else if (inRetryScope)
1400                                {
1401                                    _remainingRetries = 0;
1402                                }
1403                                current.TimesTried = GetIterationCount(request);
1404                            }
1405                            else if (inRetryScope)
1406                            {
1407                                _remainingRetries = 0;
1408                            }
1409
1410                            query.Result = current;
1411
1412                            // [DC]: Callback is for a final result, not a retry
1413                            if (_remainingRetries == 0)
1414                            {
1415                                CompleteWithQuery(query, request, callback, asyncResult);
1416                            }
1417                        };
1418                 }
1419                 UpdateRepeatingRequestState(request);
1420             }
1421             return asyncResult;
1422         }
1423 #else
1424         // TODO BeginRequest and BeginRequest<T> have too much duplication
1425         
1426         private void BeginRequestImpl(RestRequest request,
1427                                       RestCallback callback,
1428                                       WebQuery query,
1429                                       string url,
1430                                       bool isInternal,
1431                                       object userState)
1432         {
1433             request = request ?? new RestRequest();
1434             if (!isInternal)
1435             {
1436                 // [DC]: Recursive call possible, only do this once
1437                 var uri = request.BuildEndpoint(this);
1438                 query = GetQueryFor(request, uri);
1439                 SetQueryMeta(request, query);
1440                 url = uri.ToString();
1441             }
1442
1443             if (RequestExpectsMock(request))
1444             {
1445                 url = BuildMockRequestUrl(request, query, url);
1446             }
1447
1448             var retryPolicy = GetRetryPolicy(request);
1449             _remainingRetries = (retryPolicy != null
1450                                      ? retryPolicy.RetryCount
1451                                      : 0);
1452
1453             Action beginRequest;
1454             
1455             var streamOptions = GetStreamOptions(request);
1456             if (streamOptions != null)
1457             {
1458 #if !SILVERLIGHT
1459                 query.KeepAlive = true;
1460 #endif
1461                 var duration = streamOptions.Duration.HasValue
1462                                    ? streamOptions.Duration.Value
1463                                    : TimeSpan.Zero;
1464
1465                 var resultCount = streamOptions.ResultsPerCallback.HasValue
1466                                       ? streamOptions.ResultsPerCallback.Value
1467                                       : 10;
1468
1469                 beginRequest = () => BeginRequestStreamFunction(
1470                    request, query, url, callback, duration, resultCount, userState
1471                    );
1472
1473                 beginRequest.Invoke();
1474             }
1475             else
1476             {
1477                 beginRequest = ()=> BeginRequestFunction(isInternal,
1478                        request,
1479                        query,
1480                        url,
1481                        callback,
1482                        userState);
1483
1484                 beginRequest.Invoke();
1485             }
1486
1487             if (isInternal || (request.TaskOptions == null || request.TaskOptions.RepeatInterval.TotalMilliseconds == 0))
1488             {
1489                 if (request.IsFirstIteration && IsFirstIteration)
1490                 {
1491                     query.QueryResponse += (sender, args) => QueryResponseCallback(query, 
1492                                                                                retryPolicy, 
1493                                                                                request, 
1494                                                                                callback, 
1495                                                                                url, 
1496                                                                                userState);
1497                 }
1498                 UpdateRepeatingRequestState(request); 
1499                 UpdateRetryState(request);
1500             }
1501         }
1502
1503         private void BeginRequestImpl<T>(RestRequest request,
1504                                          RestCallback<T> callback,
1505                                          WebQuery query,
1506                                          string url,
1507                                          bool isInternal,
1508                                          object userState)
1509         {
1510             request = request ?? new RestRequest();
1511             if (!isInternal)
1512             {
1513                 var uri = request.BuildEndpoint(this);
1514                 query = GetQueryFor(request, uri);
1515                 SetQueryMeta(request, query);
1516                 url = uri.ToString();
1517             }
1518
1519             if (RequestExpectsMock(request))
1520             {
1521                 url = BuildMockRequestUrl(request, query, url);
1522             }
1523
1524             var retryPolicy = GetRetryPolicy(request);
1525             _remainingRetries = (retryPolicy != null
1526                                      ? retryPolicy.RetryCount
1527                                      : 0);
1528
1529             Action beginRequest;
1530             var streamOptions = GetStreamOptions(request);
1531             if (streamOptions != null)
1532             {
1533 #if !SILVERLIGHT
1534                 query.KeepAlive = true;
1535 #endif
1536
1537                 var duration = streamOptions.Duration.HasValue
1538                                    ? streamOptions.Duration.Value
1539                                    : TimeSpan.Zero;
1540
1541                 var resultCount = streamOptions.ResultsPerCallback.HasValue
1542                                       ? streamOptions.ResultsPerCallback.Value
1543                                       : 10;
1544
1545                 beginRequest = () => BeginRequestStreamFunction(
1546                    request, query, url, callback, duration, resultCount, userState
1547                    );
1548                 
1549                 beginRequest.Invoke();
1550             }
1551             else
1552             {
1553                 beginRequest = () => BeginRequestFunction(
1554                    isInternal, request, query, url, callback, userState
1555                    );
1556                 
1557                 beginRequest.Invoke();
1558             }
1559             if (IsFirstIteration && request.IsFirstIteration)
1560             {
1561                 query.QueryResponse += (sender, args) => QueryResponseCallback(query,
1562                                                                            retryPolicy,
1563                                                                            request,
1564                                                                            callback,
1565                                                                            url,
1566                                                                            userState);
1567             }
1568             UpdateRepeatingRequestState(request);
1569             UpdateRetryState(request);
1570         }
1571         
1572         private void QueryResponseCallback(WebQuery query,
1573                                            RetryPolicy retryPolicy,
1574                                            RestRequest request,
1575                                            RestCallback callback,
1576                                            string url,
1577                                            object userState)
1578         {
1579             var current = query.Result;
1580
1581             if (retryPolicy != null)
1582             {
1583                 // [DC]: Query should already have exception applied
1584                 var exception = query.Result.Exception;
1585                 var retry = _remainingRetries > 0 && ShouldRetry(retryPolicy, exception, current);
1586
1587                 if (retry)
1588                 {
1589                     BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
1590                     Interlocked.Decrement(ref _remainingRetries);
1591                 }
1592                 else
1593                 {
1594                     _remainingRetries = 0;
1595                 }
1596                 current.TimesTried = GetIterationCount(request);
1597             }
1598             else
1599             {
1600                 _remainingRetries = 0;
1601             }
1602
1603             query.Result = current;
1604
1605             // [DC]: Callback is for a final result, not a retry
1606             if (_remainingRetries == 0)
1607             {
1608                 CompleteWithQuery(query, request, callback);
1609             }
1610         }
1611         private void QueryResponseCallback<T>(WebQuery query,
1612                                               RetryPolicy retryPolicy,
1613                                               RestRequest request,
1614                                               RestCallback<T> callback,
1615                                               string url,
1616                                               object userState)
1617         {
1618             var current = query.Result;
1619
1620             if (retryPolicy != null)
1621             {
1622                 // [DC]: Query should already have exception applied
1623                 var exception = query.Result.Exception;
1624                 var retry = _remainingRetries > 0 && ShouldRetry(retryPolicy, exception, current);
1625
1626                 if (retry)
1627                 {
1628                     BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
1629                     Interlocked.Decrement(ref _remainingRetries);
1630                 }
1631                 else
1632                 {
1633                     _remainingRetries = 0;
1634                 }
1635                 current.TimesTried = GetIterationCount(request);
1636
1637             }
1638             else
1639             {
1640                 _remainingRetries = 0;
1641             }
1642
1643             query.Result = current;
1644
1645             // [DC]: Callback is for a final result, not a retry
1646             if (_remainingRetries == 0)
1647             {
1648                 CompleteWithQuery(query, request, callback);
1649             }
1650         }
1651 #endif
1652
1653 #if !WindowsPhone
1654         private WebQueryAsyncResult BeginRequestFunction(bool isInternal,
1655                                                          RestRequest request,
1656                                                          WebQuery query,
1657                                                          string url,
1658                                                          RestCallback callback,
1659                                                          object userState)
1660         {
1661             WebQueryAsyncResult result;
1662             if (!isInternal)
1663             {
1664                 if (!BeginRequestWithTask(request, callback, query, url, out result, userState))
1665                 {
1666                     if (!BeginRequestWithCache(request, query, url, out result, userState))
1667                     {
1668                         if (!BeginRequestMultiPart(request, query, url, out result, userState))
1669                         {
1670                             // Normal operation
1671                             result = query.RequestAsync(url, userState);
1672                         }
1673                     }
1674                 }
1675             }
1676             else
1677             {
1678                 // Normal operation
1679                 result = query.RequestAsync(url, userState);
1680             }
1681
1682             result.Tag = new Triplet<RestRequest, RestCallback, object>
1683             {
1684                 First = request,
1685                 Second = callback,
1686                 Third = userState
1687             };
1688
1689             return result;
1690         }
1691 #else
1692         private void BeginRequestFunction(bool isInternal,
1693                                           RestRequest request,
1694                                           WebQuery query,
1695                                           string url,
1696                                           RestCallback callback,
1697                                           object userState)
1698         {
1699             if (!isInternal)
1700             {
1701                 if (!BeginRequestWithTask(request, callback, query, url, userState))
1702                 {
1703                     if (!BeginRequestWithCache(request, query, url, userState))
1704                     {
1705                         if (!BeginRequestMultiPart(request, query, url, userState))
1706                         {
1707                             // Normal operation
1708                             query.RequestAsync(url, userState);
1709                         }
1710                     }
1711                 }
1712             }
1713             else
1714             {
1715                 // Normal operation
1716                 query.RequestAsync(url, userState);
1717             }
1718         }
1719 #endif
1720
1721         private void SignalTasks(RestRequest request, WebQueryAsyncResult result)
1722         {
1723             // Recurring tasks are only signalled when cancelled 
1724             // or when they reach their iteration limit
1725             lock (_timedTasksLock)
1726             {
1727                 if (!_tasks.ContainsKey(request))
1728                 {
1729                     result.Signal();
1730                 }
1731             }
1732         }
1733
1734 #if !WindowsPhone
1735
1736 #if NET40
1737         private void CompleteWithQueryDynamic(WebQuery query,
1738                                                  RestRequest request,
1739                                                  RestCallback<dynamic> callback,
1740                                                  WebQueryAsyncResult result)
1741         {
1742             var response = BuildResponseFromResultDynamic(request, query);
1743
1744             CompleteWithQuery(request, query, callback, response, result);
1745         }
1746 #endif
1747
1748         private void CompleteWithQuery<T>(WebQuery query,
1749                                           RestRequest request,
1750                                           RestCallback<T> callback,
1751                                           WebQueryAsyncResult result)
1752         {
1753             var response = BuildResponseFromResult<T>(request, query);
1754
1755             CompleteWithQuery(request, query, callback, response, result);
1756         }
1757
1758         private void CompleteWithQuery<T>(RestRequest request, WebQuery query, RestCallback<T> callback, RestResponse<T> response, WebQueryAsyncResult result)
1759         {
1760             if (query.IsStreaming && callback != null)
1761             {
1762                 callback.Invoke(request, response, query.UserState);
1763                 return;
1764             }
1765
1766             var wasStreaming = response.Content.Equals(EndStreamingContent);
1767
1768             result.AsyncState = response;
1769             result.IsCompleted = true;
1770             if (callback != null && !wasStreaming)
1771             {
1772                 callback.Invoke(request, response, query.UserState);
1773             }
1774
1775             if (wasStreaming)
1776             {
1777                 _streamQuery = null;
1778             }
1779
1780             SignalTasks(request, result);
1781         }
1782
1783         private void CompleteWithQuery(WebQuery query,
1784                                        RestRequest request,
1785                                        RestCallback callback,
1786                                        WebQueryAsyncResult result)
1787         {
1788             var response = BuildResponseFromResult(request, query);
1789             if (query.IsStreaming && callback != null)
1790             {
1791                 callback.Invoke(request, response, query.UserState);
1792                 return;
1793             }
1794
1795             var wasStreaming = response.Content.Equals(EndStreamingContent);
1796
1797             result.AsyncState = response;
1798             result.IsCompleted = true;
1799             if (callback != null && !wasStreaming)
1800             {
1801                 callback.Invoke(request, response, query.UserState);
1802             }
1803
1804             if (wasStreaming)
1805             {
1806                 _streamQuery = null;
1807             }
1808
1809             SignalTasks(request, result);
1810         }
1811 #else
1812         private void CompleteWithQuery<T>(WebQuery query,
1813                                           RestRequest request,
1814                                           RestCallback<T> callback)
1815         {
1816             var response = BuildResponseFromResult<T>(request, query);
1817             if (query.IsStreaming)
1818             {
1819                 return;
1820             }
1821
1822             var wasStreaming = response.Content.Equals("END STREAMING");
1823
1824             if (callback != null && !wasStreaming)
1825             {
1826                 callback.Invoke(request, response, query.UserState);
1827             }
1828         }
1829         private void CompleteWithQuery(WebQuery query,
1830                                        RestRequest request,
1831                                        RestCallback callback)
1832         {
1833             var response = BuildResponseFromResult(request, query);
1834             if (query.IsStreaming)
1835             {
1836                 return;
1837             }
1838
1839             var wasStreaming = response.Content.Equals("END STREAMING");
1840
1841             if (callback != null && !wasStreaming)
1842             {
1843                 callback.Invoke(request, response, query.UserState);
1844             }
1845         }
1846 #endif
1847
1848 #if !WindowsPhone
1849         private WebQueryAsyncResult BeginRequestFunction<T>(bool isInternal,
1850                                                             RestRequest request,
1851                                                             WebQuery query,
1852                                                             string url,
1853                                                             RestCallback<T> callback,
1854                                                             object userState)
1855         {
1856
1857             WebQueryAsyncResult result;
1858             if (!isInternal)
1859             {
1860                 if (!BeginRequestWithTask(request, callback, query, url, out result, userState))
1861                 {
1862                     if (!BeginRequestWithCache(request, query, url, out result, userState))
1863                     {
1864                         if (!BeginRequestMultiPart(request, query, url, out result, userState))
1865                         {
1866                             // Normal operation
1867                             result = query.RequestAsync(url, userState);
1868                         }
1869                     }
1870                 }
1871             }
1872             else
1873             {
1874                 // Normal operation
1875                 result = query.RequestAsync(url, userState);
1876             }
1877
1878             result.Tag = new Triplet<RestRequest, RestCallback<T>, object>
1879             {
1880                 First = request,
1881                 Second = callback,
1882                 Third = userState
1883             };
1884             return result;
1885         }
1886 #else
1887         private void BeginRequestFunction<T>(bool isInternal,
1888                                              RestRequest request,
1889                                              WebQuery query,
1890                                              string url,
1891                                              RestCallback<T> callback,
1892                                              object userState)
1893         {
1894
1895             if (!isInternal)
1896             {
1897                 if (!BeginRequestWithTask(request, callback, query, url, userState))
1898                 {
1899                     if (!BeginRequestWithCache(request, query, url, userState))
1900                     {
1901                         if (!BeginRequestMultiPart(request, query, url, userState))
1902                         {
1903                             // Normal operation
1904                             query.RequestAsync(url, userState);
1905                         }
1906                     }
1907                 }
1908             }
1909             else
1910             {
1911                 // Normal operation
1912                 query.RequestAsync(url, userState);
1913             }
1914         }
1915 #endif
1916         private WebQueryAsyncResult BeginRequestStreamFunction<T>(RestRequest request,
1917                                                                   WebQuery query,
1918                                                                   string url,
1919                                                                   RestCallback<T> callback,
1920                                                                   TimeSpan duration,
1921                                                                   int resultsPerCallback,
1922                                                                   object userState)
1923         {
1924             var result = GetStreamResult(request, query, url, duration, resultsPerCallback);
1925
1926             result.Tag = new Triplet<RestRequest, RestCallback<T>, object>
1927             {
1928                 First = request,
1929                 Second = callback,
1930                 Third = userState
1931             };
1932
1933             return result;
1934         }
1935         private WebQueryAsyncResult BeginRequestStreamFunction(RestRequest request,
1936                                                                       WebQuery query,
1937                                                                       string url,
1938                                                                       RestCallback callback,
1939                                                                       TimeSpan duration,
1940                                                                       int resultsPerCallback,
1941                                                                       object userState)
1942         {
1943             var result = GetStreamResult(request, query, url, duration, resultsPerCallback);
1944
1945             result.Tag = new Triplet<RestRequest, RestCallback, object>
1946             {
1947                 First = request,
1948                 Second = callback,
1949                 Third = userState
1950             };
1951
1952             return result;
1953         }
1954
1955         private WebQueryAsyncResult GetStreamResult(RestBase request,
1956                                                     WebQuery query,
1957                                                     string url,
1958                                                     TimeSpan duration,
1959                                                     int resultsPerCallback)
1960         {
1961             if (!request.Method.HasValue)
1962             {
1963                 request.Method = WebMethod.Get;
1964             }
1965
1966             WebQueryAsyncResult result;
1967             switch (request.Method)
1968             {
1969                 case WebMethod.Get:
1970                     result = query.ExecuteStreamGetAsync(url, duration, resultsPerCallback);
1971                     break;
1972                 case WebMethod.Post:
1973                     result = query.ExecuteStreamPostAsync(url, duration, resultsPerCallback);
1974                     break;
1975                 default:
1976                     throw new NotSupportedException("Unsupported HTTP method declared for streaming.");
1977             }
1978
1979             _streamQuery = query;
1980
1981             return result;
1982         }
1983
1984         private void RegisterTimedTaskForRequest(RestRequest request, TimedTask task)
1985         {
1986             lock (_timedTasksLock)
1987             {
1988                 if (_tasks.ContainsKey(request))
1989                 {
1990                     throw new InvalidOperationException("Task already has a registered timed task");
1991                 }
1992                 task.Stopped += (s, e) => UnregisterTimedTaskForRequest(request);
1993                 _tasks.Add(request, task);
1994             }
1995         }
1996
1997         private void UnregisterTimedTaskForRequest(RestRequest request)
1998         {
1999             lock (_timedTasksLock)
2000             {
2001                 if (_tasks.ContainsKey(request))
2002                 {
2003                     var task = _tasks[request];
2004                     _tasks.Remove(request);
2005                     task.Dispose();
2006                 }
2007             }
2008         }
2009
2010 #if !WindowsPhone
2011         private bool BeginRequestWithTask(RestRequest request,
2012                                           RestCallback callback,
2013                                           WebQuery query,
2014                                           string url,
2015                                           out WebQueryAsyncResult asyncResult,
2016                                           object userState)
2017         {
2018             var taskOptions = GetTaskOptions(request);
2019             if (taskOptions == null)
2020             {
2021                 asyncResult = null;
2022                 return false;
2023             }
2024
2025             if (taskOptions.RepeatInterval <= TimeSpan.Zero)
2026             {
2027                 asyncResult = null;
2028                 return false;
2029             }
2030
2031             TimedTask task;
2032 #if !NETCF
2033             if (!taskOptions.GetType().IsGenericType)
2034             {
2035 #endif
2036                 // Tasks without rate limiting
2037                 task = new TimedTask(taskOptions.DueTime,
2038                                      taskOptions.RepeatInterval,
2039                                      taskOptions.RepeatTimes,
2040                                      taskOptions.ContinueOnError,
2041                                      skip => BeginRequestImpl(request,
2042                                                           callback,
2043                                                           query,
2044                                                           url,
2045                                                           true /* isInternal */,
2046                                                           userState
2047                                                           ));
2048
2049 #if !NETCF
2050             }
2051             else
2052             {
2053                 // Tasks with rate limiting
2054                 task = (TimedTask)BuildRateLimitingTask(request,
2055                                                  taskOptions,
2056                                                  callback,
2057                                                  query,
2058                                                  url,
2059                                                  userState);
2060             }
2061 #endif
2062
2063             RegisterTimedTaskForRequest(request, task);
2064
2065             Action action = task.Start;
2066
2067             var inner = action.BeginInvoke(ar => {/* No callback */}, null);
2068
2069             asyncResult = new WebQueryAsyncResult { InnerResult = inner };
2070             task.AsyncResult = asyncResult;
2071             return true;
2072         }
2073
2074         private bool BeginRequestWithTask<T>(RestRequest request,
2075                                           RestCallback<T> callback,
2076                                           WebQuery query,
2077                                           string url,
2078                                           out WebQueryAsyncResult asyncResult,
2079                                           object userState)
2080         {
2081             var taskOptions = GetTaskOptions(request);
2082             if (taskOptions == null)
2083             {
2084                 asyncResult = null;
2085                 return false;
2086             }
2087
2088             if (taskOptions.RepeatInterval <= TimeSpan.Zero)
2089             {
2090                 asyncResult = null;
2091                 return false;
2092             }
2093
2094             TimedTask task;
2095 #if !NETCF
2096             if (!taskOptions.GetType().IsGenericType)
2097             {
2098 #endif
2099                 // Tasks without rate limiting
2100                 task = new TimedTask(taskOptions.DueTime,
2101                                       taskOptions.RepeatInterval,
2102                                       taskOptions.RepeatTimes,
2103                                       taskOptions.ContinueOnError,
2104                                       skip => BeginRequestImpl(request,
2105                                                                callback,
2106                                                                query,
2107                                                                url,
2108                                                                true /* isInternal */,
2109                                                                userState));
2110 #if !NETCF
2111             }
2112             else
2113             {
2114                 // Tasks with rate limiting
2115                 task = (TimedTask)BuildRateLimitingTask(request,
2116                                                  taskOptions,
2117                                                  callback,
2118                                                  query,
2119                                                  url,
2120                                                  userState);
2121
2122             }
2123 #endif
2124             lock (_timedTasksLock)
2125             {
2126                 _tasks[request] = task;
2127             }
2128             var action = new Action(task.Start);
2129
2130             var inner = action.BeginInvoke(ar => { /* No callback */ }, null);
2131             asyncResult = new WebQueryAsyncResult { InnerResult = inner };
2132             task.AsyncResult = asyncResult;
2133             return true;
2134         }
2135 #else
2136         private bool BeginRequestWithTask(RestRequest request,
2137                                           RestCallback callback,
2138                                           WebQuery query,
2139                                           string url,
2140                                           object userState)
2141         {
2142             var taskOptions = GetTaskOptions(request);
2143             if (taskOptions == null)
2144             {
2145                 return false;
2146             }
2147
2148             if (taskOptions.RepeatInterval <= TimeSpan.Zero)
2149             {
2150                 return false;
2151             }
2152
2153             TimedTask task;
2154             if (!taskOptions.GetType().IsGenericType)
2155             {
2156                 // Tasks without rate limiting
2157                 task = new TimedTask(taskOptions.DueTime,
2158                                      taskOptions.RepeatInterval,
2159                                      taskOptions.RepeatTimes,
2160                                      taskOptions.ContinueOnError,
2161                                      skip => BeginRequestImpl(request,
2162                                                               callback,
2163                                                               query,
2164                                                               url,
2165                                                               true /* isInternal */,
2166                                                               userState
2167                                                  ));
2168             }
2169             else
2170             {
2171                 // Tasks with rate limiting
2172                 task = (TimedTask)BuildRateLimitingTask(request,
2173                                                         taskOptions,
2174                                                         callback,
2175                                                         query,
2176                                                         url,
2177                                                         userState);
2178             }
2179
2180             RegisterTimedTaskForRequest(request, task);
2181
2182             var action = new Action(task.Start);
2183             action.Invoke();
2184             return true;
2185         }
2186
2187         private bool BeginRequestWithTask<T>(RestRequest request,
2188                                              RestCallback<T> callback,
2189                                              WebQuery query,
2190                                              string url,
2191                                              object userState)
2192         {
2193             var taskOptions = GetTaskOptions(request);
2194             if (taskOptions == null)
2195             {
2196                 return false;
2197             }
2198
2199             if (taskOptions.RepeatInterval <= TimeSpan.Zero)
2200             {
2201                 return false;
2202             }
2203
2204             TimedTask task;
2205             if (!taskOptions.GetType().IsGenericType)
2206             {
2207                 // Tasks without rate limiting
2208                 task = new TimedTask(taskOptions.DueTime,
2209                                       taskOptions.RepeatInterval,
2210                                       taskOptions.RepeatTimes,
2211                                       taskOptions.ContinueOnError,
2212                                       skip => BeginRequestImpl(request,
2213                                                                callback,
2214                                                                query,
2215                                                                url,
2216                                                                true /* isInternal */,
2217                                                                userState));
2218             }
2219             else
2220             {
2221                 // Tasks with rate limiting
2222                 task = (TimedTask)BuildRateLimitingTask(request,
2223                                                  taskOptions,
2224                                                  callback,
2225                                                  query,
2226                                                  url,
2227                                                  userState);
2228
2229             }
2230             lock (_timedTasksLock)
2231             {
2232                 _tasks[request] = task;
2233             }
2234
2235             var action = new Action(task.Start);
2236             action.Invoke();
2237             return true;
2238         }
2239 #endif
2240
2241 #if !NETCF
2242         private object BuildRateLimitingTask(RestRequest request, ITaskOptions taskOptions, RestCallback callback, WebQuery query, string url, object userState)
2243         {
2244             var taskAction = new Action<bool>(skip =>
2245                                                   {
2246                                                       if (!skip)
2247                                                       {
2248                                                           BeginRequestImpl(request, callback, query, url, true /* isInternal */, userState);
2249                                                       }
2250                                                       else
2251                                                       {
2252                                                           callback(request,
2253                                                                    new RestResponse { SkippedDueToRateLimitingRule = true },
2254                                                                    userState);
2255                                                       }
2256                                                   });
2257
2258             return BuildRateLimitingTaskImpl(taskOptions, taskAction);
2259         }
2260
2261         private object BuildRateLimitingTask<T>(RestRequest request,
2262                                             ITaskOptions taskOptions,
2263                                             RestCallback<T> callback,
2264                                             WebQuery query,
2265                                             string url,
2266                                             object userState)
2267         {
2268             var taskAction = new Action<bool>(skip => BeginRequestImpl(request,
2269                                                                        callback,
2270                                                                        query,
2271                                                                        url,
2272                                                                        true /* isInternal */,
2273                                                                        userState
2274                                                                    ));
2275
2276             return BuildRateLimitingTaskImpl(taskOptions, taskAction);
2277         }
2278
2279         private static object BuildRateLimitingTaskImpl(ITaskOptions taskOptions,
2280                                                         Action<bool> taskAction)
2281         {
2282             var innerType = taskOptions.GetDeclaredTypeForGeneric(typeof(ITaskOptions<>));
2283             var rateType = typeof(RateLimitingRule<>).MakeGenericType(innerType);
2284             var taskType = typeof(TimedTask<>).MakeGenericType(innerType);
2285             var rateLimitingType = (RateLimitType)taskOptions.GetValue("RateLimitType");
2286
2287             object taskRule;
2288             var getRateLimitStatus = taskOptions.GetValue("GetRateLimitStatus");
2289             switch (rateLimitingType)
2290             {
2291                 case RateLimitType.ByPercent:
2292                     var rateLimitingPercent = taskOptions.GetValue("RateLimitPercent");
2293                     taskRule = getRateLimitStatus != null
2294                                    ? Activator.CreateInstance(rateType, getRateLimitStatus, rateLimitingPercent)
2295                                    : Activator.CreateInstance(rateType, rateLimitingPercent);
2296                     break;
2297                 case RateLimitType.ByPredicate:
2298                     var rateLimitingPredicate = taskOptions.GetValue("RateLimitingPredicate");
2299                     taskRule = getRateLimitStatus != null
2300                                    ? Activator.CreateInstance(rateType, getRateLimitStatus, rateLimitingPredicate)
2301                                    : Activator.CreateInstance(rateType, rateLimitingPredicate);
2302                     break;
2303                 default:
2304                     throw new ArgumentOutOfRangeException();
2305             }
2306
2307             return Activator.CreateInstance(taskType,
2308                                             taskOptions.DueTime,
2309                                             taskOptions.RepeatInterval,
2310                                             taskOptions.RepeatTimes,
2311                                             taskOptions.ContinueOnError,
2312                                             taskAction,
2313                                             taskRule);
2314         }
2315 #endif
2316
2317 #if !WindowsPhone
2318         private bool BeginRequestMultiPart(RestBase request, WebQuery query, string url, out WebQueryAsyncResult result, object userState)
2319         {
2320             var parameters = GetPostParameters(request);
2321             if (parameters == null || parameters.Count() == 0)
2322             {
2323                 result = null;
2324                 return false;
2325             }
2326
2327             // [DC]: Default to POST if no method provided
2328             query.Method = query.Method != WebMethod.Post && Method != WebMethod.Put ? WebMethod.Post : query.Method;
2329             result = query.RequestAsync(url, parameters, userState);
2330             return true;
2331         }
2332 #else
2333         private bool BeginRequestMultiPart(RestBase request, WebQuery query, string url, object userState)
2334         {
2335             var parameters = GetPostParameters(request);
2336             if (parameters == null || parameters.Count() == 0)
2337             {
2338                 return false;
2339             }
2340
2341             // [DC]: Default to POST if no method provided
2342             query.Method = query.Method != WebMethod.Post && Method != WebMethod.Put ? WebMethod.Post : query.Method;
2343             query.RequestAsync(url, parameters, userState);
2344             return true;
2345         }
2346 #endif
2347
2348 #if !WindowsPhone
2349         private bool BeginRequestWithCache(RestBase request,
2350                                            WebQuery query,
2351                                            string url,
2352                                            out WebQueryAsyncResult result,
2353                                            object userState)
2354         {
2355             var cache = GetCache(request);
2356             if (cache == null)
2357             {
2358                 result = null;
2359                 return false;
2360             }
2361
2362             var options = GetCacheOptions(request);
2363             if (options == null)
2364             {
2365                 result = null;
2366                 return false;
2367             }
2368
2369             // [DC]: This is currently prefixed to the full URL
2370             var function = GetCacheKeyFunction(request);
2371             var key = function != null ? function.Invoke() : "";
2372
2373             switch (options.Mode)
2374             {
2375                 case CacheMode.NoExpiration:
2376                     result = query.RequestAsync(url, key, cache, userState);
2377                     break;
2378                 case CacheMode.AbsoluteExpiration:
2379                     var expiry = options.Duration.FromNow();
2380                     result = query.RequestAsync(url, key, cache, expiry, userState);
2381                     break;
2382                 case CacheMode.SlidingExpiration:
2383                     result = query.RequestAsync(url, key, cache, options.Duration, userState);
2384                     break;
2385                 default:
2386                     throw new NotSupportedException("Unknown CacheMode");
2387             }
2388
2389             return true;
2390         }
2391 #else
2392         private bool BeginRequestWithCache(RestBase request,
2393                                            WebQuery query,
2394                                            string url,
2395                                            object userState)
2396         {
2397             var cache = GetCache(request);
2398             if (cache == null)
2399             {
2400                 return false;
2401             }
2402
2403             var options = GetCacheOptions(request);
2404             if (options == null)
2405             {
2406                 return false;
2407             }
2408
2409             // [DC]: This is currently prefixed to the full URL
2410             var function = GetCacheKeyFunction(request);
2411             var key = function != null ? function.Invoke() : "";
2412
2413             switch (options.Mode)
2414             {
2415                 case CacheMode.NoExpiration:
2416                     query.RequestAsync(url, key, cache, userState);
2417                     break;
2418                 case CacheMode.AbsoluteExpiration:
2419                     var expiry = options.Duration.FromNow();
2420                     query.RequestAsync(url, key, cache, expiry, userState);
2421                     break;
2422                 case CacheMode.SlidingExpiration:
2423                     query.RequestAsync(url, key, cache, options.Duration, userState);
2424                     break;
2425                 default:
2426                     throw new NotSupportedException("Unknown CacheMode");
2427             }
2428
2429             return true;
2430         }
2431 #endif
2432
2433         private RestResponse BuildResponseFromResult(RestRequest request, WebQuery query)
2434         {
2435             request = request ?? new RestRequest();
2436             var result = query.Result;
2437             var response = BuildBaseResponse(result);
2438
2439             DeserializeEntityBody(request, response);
2440             response.Tag = GetTag(request);
2441
2442             return response;
2443         }
2444
2445         private RestResponse<T> BuildResponseFromResult<T>(RestRequest request, WebQuery query)
2446         {
2447             request = request ?? new RestRequest();
2448             var result = query.Result;
2449             var response = BuildBaseResponse<T>(result);
2450
2451             DeserializeEntityBody(request, response);
2452             response.Tag = GetTag(request);
2453
2454             return response;
2455         }
2456
2457 #if NET40
2458         private RestResponse<dynamic> BuildResponseFromResultDynamic(RestRequest request, WebQuery query)
2459         {
2460             request = request ?? new RestRequest();
2461             var result = query.Result;
2462             var response = BuildBaseResponse<dynamic>(result);
2463
2464             DeserializeEntityBodyDynamic(request, response);
2465             response.Tag = GetTag(request);
2466
2467             return response;
2468         }
2469 #endif
2470
2471         private static readonly Func<RestResponseBase, WebQueryResult, RestResponseBase> _baseSetter =
2472                 (response, result) =>
2473                 {
2474                     response.ContentStream = result.ContentStream;
2475                     response.InnerResponse = result.WebResponse;
2476                     response.InnerException = result.Exception;
2477                     response.RequestDate = result.RequestDate;
2478                     response.RequestUri = result.RequestUri;
2479                     response.RequestMethod = result.RequestHttpMethod;
2480                     response.RequestKeptAlive = result.RequestKeptAlive;
2481                     response.ResponseDate = result.ResponseDate;
2482                     response.ResponseUri = result.ResponseUri;
2483                     response.StatusCode = (HttpStatusCode)result.ResponseHttpStatusCode;
2484                     response.StatusDescription = result.ResponseHttpStatusDescription;
2485                     response.ContentType = result.ResponseType;
2486                     response.ContentLength = result.ResponseLength;
2487                     response.IsMock = result.IsMock;
2488                     response.TimedOut = result.TimedOut;
2489                     response.TimesTried = result.TimesTried;
2490                     if (result.WebResponse == null)
2491                     {
2492                         // [DC] WebResponse could be null, i.e. when streaming
2493                         return response;
2494                     }
2495 #if !SILVERLIGHT
2496                     response.Headers = result.WebResponse.Headers;
2497 #else
2498                     response.Headers = new NameValueCollection();
2499                     foreach(var key in result.WebResponse.Headers.AllKeys)
2500                     {
2501                         response.Headers.Add(key, result.WebResponse.Headers[key]);
2502                     }
2503 #endif
2504
2505 #if !SILVERLIGHT && !NETCF
2506                     if(result.WebResponse is HttpWebResponse)
2507                     {
2508                         var cookies = (result.WebResponse as HttpWebResponse).Cookies;
2509                         if(cookies != null)
2510                         {
2511                             foreach (Cookie cookie in cookies)
2512                             {
2513                                 response.Cookies.Add(cookie.Name, cookie.Value);
2514                             }
2515                         }
2516                     }
2517 #endif
2518
2519                     return response;
2520                 };
2521
2522         private static RestResponse BuildBaseResponse(WebQueryResult result)
2523         {
2524             var response = new RestResponse();
2525
2526             _baseSetter.Invoke(response, result);
2527
2528             return response;
2529         }
2530
2531         private static RestResponse<T> BuildBaseResponse<T>(WebQueryResult result)
2532         {
2533             var response = new RestResponse<T>();
2534
2535             _baseSetter.Invoke(response, result);
2536
2537             return response;
2538         }
2539
2540         private bool ShouldDeserializeEntityBody(RestRequest request, RestResponseBase response, out IDeserializer deserializer)
2541         {
2542             deserializer = request.Deserializer ?? Deserializer;
2543             if (deserializer == null || response.ContentStream == null || string.IsNullOrEmpty(response.ContentType))
2544             {
2545                 return false;
2546             }
2547             if (response.InnerException != null)
2548             {
2549                 Type errorResponseEntityType;
2550                 var getErrorResponseEntityType = request.GetErrorResponseEntityType ?? GetErrorResponseEntityType;
2551                 if (getErrorResponseEntityType != null && (errorResponseEntityType = getErrorResponseEntityType(request, response)) != null)
2552                 {
2553                     response.ErrorContentEntity = deserializer.Deserialize(response, errorResponseEntityType);
2554                 }
2555                 return false;
2556             }
2557
2558             return true;
2559         }
2560
2561         private void DeserializeEntityBody(RestRequest request, RestResponse response)
2562         {
2563             IDeserializer deserializer;
2564             if (ShouldDeserializeEntityBody(request, response, out deserializer) && request.ResponseEntityType != null)
2565             {
2566                 response.ContentEntity = deserializer.Deserialize(response, request.ResponseEntityType);
2567             }
2568         }
2569
2570         private void DeserializeEntityBody<T>(RestRequest request, RestResponse<T> response)
2571         {
2572             IDeserializer deserializer;
2573             if (ShouldDeserializeEntityBody(request, response, out deserializer))
2574             {
2575                 response.ContentEntity = deserializer.Deserialize<T>(response);
2576             }
2577         }
2578
2579 #if NET40
2580         private void DeserializeEntityBodyDynamic(RestRequest request, RestResponse<dynamic> response)
2581         {
2582             IDeserializer deserializer;
2583             if (ShouldDeserializeEntityBody(request, response, out deserializer))
2584             {
2585                 response.ContentEntity = deserializer.DeserializeDynamic(response);
2586             }
2587         }
2588 #endif
2589
2590         private void SetQueryMeta(RestRequest request, WebQuery query)
2591         {
2592             // Fill query collections with found value pairs
2593             CoalesceWebPairsIntoCollection(query.Parameters, Parameters, request.Parameters);
2594             CoalesceWebPairsIntoCollection(query.Cookies, Cookies, request.Cookies);
2595             
2596             query.Headers.AddRange(Headers);
2597             query.Headers.AddRange(request.Headers);
2598
2599             // [DC]: These properties are trumped by request over client
2600             query.UserAgent = GetUserAgent(request);
2601             query.Method = GetWebMethod(request);
2602             query.Proxy = GetProxy(request);
2603             query.RequestTimeout = GetTimeout(request);
2604             query.DecompressionMethods = GetDecompressionMethods(request);
2605             query.PostContent = GetPostContent(request);
2606             query.Encoding = GetEncoding(request);
2607
2608 #if !SILVERLIGHT
2609             query.FollowRedirects = GetFollowRedirects(request);
2610 #endif
2611             SerializeEntityBody(query, request);
2612         }
2613
2614         // [DC]: Trump duplicates by request over client over info values
2615         private static void CoalesceWebPairsIntoCollection(WebPairCollection target, params IEnumerable<WebPair>[] values)
2616         {
2617             var parameters = new WebPairCollection(values.SelectMany(value => value));
2618
2619             foreach (var pair in parameters)
2620             {
2621                 if(target[pair.Name] == null)
2622                 {
2623                     target.Add(pair);
2624                 }
2625             }
2626         }
2627
2628         private void SerializeEntityBody(WebQuery query, RestRequest request)
2629         {
2630             var serializer = GetSerializer(request);
2631             if (serializer == null)
2632             {
2633                 // No suitable serializer for entity
2634                 return;
2635             }
2636
2637             if (request.Entity == null || request.RequestEntityType == null)
2638             {
2639                 // Not enough information to serialize
2640                 return;
2641             }
2642
2643             var entityBody = serializer.Serialize(request.Entity, request.RequestEntityType);
2644             query.Entity = !entityBody.IsNullOrBlank()
2645                                ? new WebEntity
2646                                      {
2647                                          Content = entityBody,
2648                                          ContentEncoding = serializer.ContentEncoding,
2649                                          ContentType = serializer.ContentType
2650                                      }
2651                                : null;
2652         }
2653
2654         private WebEntity SerializeExpectEntity(RestRequest request)
2655         {
2656             var serializer = GetSerializer(request);
2657             if (serializer == null || request.ExpectEntity == null)
2658             {
2659                 // No suitable serializer or entity
2660                 return null;
2661             }
2662
2663             var entityBody = serializer.Serialize(request.ExpectEntity, request.RequestEntityType);
2664             var entity = !entityBody.IsNullOrBlank()
2665                                ? new WebEntity
2666                                {
2667                                    Content = entityBody,
2668                                    ContentEncoding = serializer.ContentEncoding,
2669                                    ContentType = serializer.ContentType
2670                                } : null;
2671             return entity;
2672         }
2673
2674         public WebQuery GetQueryFor(RestBase request, Uri uri)
2675         {
2676             var method = GetWebMethod(request);
2677             var credentials = GetWebCredentials(request);
2678             var info = GetInfo(request);
2679             var traceEnabled = GetTraceEnabled(request);
2680
2681             // [DC]: UserAgent is set via Info
2682             // [DC]: Request credentials trump client credentials
2683             var query = credentials != null
2684                             ? credentials.GetQueryFor(uri.ToString(), request, info, method, traceEnabled)
2685                             : new BasicAuthWebQuery(info, traceEnabled);
2686
2687             query.PostProgress += QueryPostProgress;
2688
2689 #if SILVERLIGHT
2690             query.HasElevatedPermissions = HasElevatedPermissions;
2691             query.SilverlightAcceptEncodingHeader = SilverlightAcceptEncodingHeader;
2692             query.SilverlightUserAgentHeader = SilverlightUserAgentHeader;
2693 #endif
2694             return query;
2695         }
2696
2697         private void QueryPostProgress(object sender, PostProgressEventArgs e)
2698         {
2699             var args = new FileProgressEventArgs
2700                            {
2701                                FileName = e.FileName,
2702                                BytesWritten = e.BytesWritten,
2703                                TotalBytes = e.TotalBytes
2704                            };
2705             OnFileProgress(args);
2706         }
2707
2708         public void CancelStreaming()
2709         {
2710             lock (_streamingLock)
2711             {
2712                 if (_streamQuery != null)
2713                 {
2714                     _streamQuery.IsStreaming = false;
2715                 }
2716             }
2717         }
2718
2719         public void CancelPeriodicTasks()
2720         {
2721             lock (_timedTasksLock)
2722             {
2723                 // Copy to a new list, since canceling 
2724                 // the task removes it from the _tasks
2725                 // list, the enumeration will throw
2726                 var toCancel = new List<TimedTask>();
2727                 toCancel.AddRange(_tasks.Values);
2728                 toCancel.ForEach(t => t.Stop());
2729             }
2730         }
2731
2732         public int GetIterationCount(RestRequest request)
2733         {
2734             var retryState = request.RetryState ?? RetryState;
2735             var taskState = request.TaskState ?? TaskState;
2736             if (retryState != null)
2737             {
2738                 return retryState.RepeatCount;
2739             }
2740             if (taskState != null)
2741             {
2742                 return taskState.RepeatCount;
2743             }
2744             return 0;
2745         }
2746
2747     }
2748 }