Fixed current operation cancellation
[pithos-ms-client] / trunk / Pithos.Network / WebExtensions.cs
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics.Contracts;
5 using System.Linq;
6 using System.Text;
7 using System.Net;
8 using System.IO;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using log4net;
12
13 namespace Pithos.Network
14 {
15     public static class WebExtensions
16     {
17
18         public static string ReadToEnd(this HttpWebResponse response)
19         {
20             using (var stream = response.GetResponseStream())
21             {
22                 if (stream == null)
23                     return null;
24                 using (var reader = new StreamReader(stream))
25                 {
26                     var body = reader.ReadToEnd();
27                     return body;
28                 }
29             }
30         }
31
32         public static void LogError(this ILog log, HttpWebResponse response)
33         {
34             if (log.IsDebugEnabled)
35             {
36                 if (response != null)
37                 {
38                     var body = response.ReadToEnd();
39                     log.ErrorFormat("Headers:\n{0}\nBody:{1}", response.Headers, body);
40                 }
41             }
42         }
43
44         public static TextReader GetLoggedReader(this Stream stream, ILog log)
45         {
46             var reader = new StreamReader(stream);
47             if (!log.IsDebugEnabled)
48                 return reader;
49
50             using (reader)
51             {
52                 var body = reader.ReadToEnd();
53                 log.DebugFormat("JSON response: {0}", body);
54                 return new StringReader(body);
55             }
56         }
57
58
59         public static TOut NullSafe<TIn, TOut>(this TIn obj, Func<TIn, TOut> memberAction)
60         {
61             //Note we should not use obj != null because it can not test value types and also
62             //compiler has to lift the type to a nullable type for doing the comparision with null.
63             return (!EqualityComparer<TIn>.Default.Equals(obj, default(TIn))) ? memberAction(obj) : default(TOut);
64
65         }
66
67         public static IEnumerable<T> Range<T>(this IList<T> list, int start, int end)
68         {
69             Contract.Requires(start >= 0);
70             Contract.Requires(end < list.Count);
71             Contract.Requires(start <= end);
72
73             if (list == null)
74                 yield break;
75
76             for (var i = 0; i <= end; i++)
77             {
78                 yield return list[i];
79             }
80
81         }
82
83         public static Task<byte[]> UploadDataTaskAsync(this WebClient webClient, Uri address, string method, byte[] data, CancellationToken cancellationToken, IProgress<UploadProgressChangedEventArgs> progress)
84         {
85             var tcs = new TaskCompletionSource<byte[]>(address);
86             if (cancellationToken.IsCancellationRequested)
87             {
88                 tcs.TrySetCanceled();
89             }
90             else
91             {
92                 CancellationTokenRegistration ctr = cancellationToken.Register(()=>
93                 {
94                     webClient.CancelAsync();
95                 });
96                 UploadDataCompletedEventHandler completedHandler = null;
97                 UploadProgressChangedEventHandler progressHandler = null;
98                 if (progress != null)
99                     progressHandler = (s, e) => PithosEAPCommon.HandleProgress(tcs, e, () => e, progress);
100                 completedHandler =(sender, e) =>PithosEAPCommon.HandleCompletion(tcs, true, e,() => e.Result,() =>
101                 { 
102                     ctr.Dispose();
103                     webClient.UploadDataCompleted -= completedHandler;
104                     webClient.UploadProgressChanged -= progressHandler;
105                 });
106                 webClient.UploadDataCompleted += completedHandler;
107                 webClient.UploadProgressChanged += progressHandler;
108                 try
109                 {
110                     webClient.UploadDataAsync(address, method, data, tcs);
111                     if (cancellationToken.IsCancellationRequested)
112                         webClient.CancelAsync();
113                 }
114                 catch
115                 {
116                     webClient.UploadDataCompleted -= completedHandler;
117                     webClient.UploadProgressChanged -= progressHandler;
118                     throw;
119                 }
120             }
121             return tcs.Task;
122         }
123
124     }
125
126     internal static class PithosEAPCommon
127     {
128         internal static void HandleProgress<T, E>(TaskCompletionSource<T> tcs, ProgressChangedEventArgs eventArgs, Func<E> getProgress, IProgress<E> callback)
129         {
130             if (eventArgs.UserState != tcs)
131                 return;
132             callback.Report(getProgress());
133         }
134
135         internal static void HandleCompletion<T>(TaskCompletionSource<T> tcs, bool requireMatch, AsyncCompletedEventArgs e, Func<T> getResult, Action unregisterHandler)
136         {
137             if (requireMatch)
138             {
139                 if (e.UserState != tcs)
140                     return;
141             }
142             try
143             {
144                 unregisterHandler();
145             }
146             finally
147             {
148                 if (e.Cancelled)
149                     tcs.TrySetCanceled();
150                 else if (e.Error != null)
151                     tcs.TrySetException(e.Error);
152                 else
153                     tcs.TrySetResult(getResult());
154             }
155         }
156     }
157
158 }