Some timeout issues
[pithos-ms-client] / trunk / Pithos.Network / WebExtensions.cs
index 5c55803..a1fb7d8 100644 (file)
@@ -4,6 +4,7 @@ using System.ComponentModel;
 using System.Diagnostics.Contracts;\r
 using System.Linq;\r
 using System.Net.Http;\r
+using System.Net.Http.Headers;\r
 using System.Reflection;\r
 using System.Text;\r
 using System.Net;\r
@@ -11,6 +12,7 @@ using System.IO;
 using System.Threading;\r
 using System.Threading.Tasks;\r
 using log4net;\r
+using System.ServiceModel.Channels;\r
 \r
 namespace Pithos.Network\r
 {\r
@@ -184,14 +186,14 @@ namespace Pithos.Network
 \r
         }\r
 \r
-        public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries)\r
+        public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries,bool acceptNotFound=false)\r
         {\r
-            return client.HeadAsyncWithRetries(requestUri, retries, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
+            return client.HeadAsyncWithRetries(requestUri, retries, acceptNotFound,HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
         }\r
 \r
-        public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
+        public static Task<HttpResponseMessage> HeadAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, bool acceptNotFound,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
         {\r
-            return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Head, requestUri), retries, completionOption, cancellationToken);\r
+            return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Head, requestUri), retries, acceptNotFound,completionOption, cancellationToken);\r
         }\r
 \r
         public static Task<HttpResponseMessage> GetAsyncWithRetries(this HttpClient client, Uri requestUri, int retries)\r
@@ -201,65 +203,139 @@ namespace Pithos.Network
 \r
         public static Task<HttpResponseMessage> GetAsyncWithRetries(this HttpClient client, Uri requestUri, int retries, HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
         {\r
-            return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Get, requestUri), retries, completionOption, cancellationToken);\r
+            return client.SendAsyncWithRetries(new HttpRequestMessage(HttpMethod.Get, requestUri), retries, false,completionOption, cancellationToken);\r
         }\r
 \r
 \r
         public static Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries)\r
         {\r
-            return client.SendAsyncWithRetries(message, retries, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
+            return client.SendAsyncWithRetries(message, retries,false, HttpCompletionOption.ResponseContentRead, CancellationToken.None);\r
         }\r
 \r
-        public static async Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
+        public static async Task<HttpResponseMessage> SendAsyncWithRetries(this HttpClient client,HttpRequestMessage message, int retries,bool acceptNotFound,HttpCompletionOption completionOption, CancellationToken cancellationToken)\r
         {\r
             var waitTime = TimeSpan.FromSeconds(10);\r
-            var acceptedCodes = new[] { HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
+            var acceptedCodes =acceptNotFound\r
+                ? new[] {HttpStatusCode.NotFound, HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
+                HttpStatusCode.RedirectMethod,HttpStatusCode.NotModified,HttpStatusCode.TemporaryRedirect,HttpStatusCode.RedirectKeepVerb,HttpStatusCode.Conflict}\r
+                : new[] { HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.Redirect,HttpStatusCode.SeeOther,\r
                 HttpStatusCode.RedirectMethod,HttpStatusCode.NotModified,HttpStatusCode.TemporaryRedirect,HttpStatusCode.RedirectKeepVerb,HttpStatusCode.Conflict};\r
+            \r
+                \r
             while (retries > 0)\r
             {\r
+                var timedOut = false;\r
                 if (Log.IsDebugEnabled)\r
-                    Log.DebugFormat("[REQUEST] {0}",message);\r
-                HttpResponseMessage result;\r
+                    Log.DebugFormat("[REQUEST] {0}", message);\r
+                HttpResponseMessage result=null;\r
+                \r
+                \r
+                \r
                 try\r
                 {\r
                     result = await client.SendAsync(message, completionOption, cancellationToken).ConfigureAwait(false);\r
                 }\r
+                catch (WebException exc)\r
+                {\r
+                    if (exc.Status != WebExceptionStatus.Timeout)\r
+                        throw;\r
+                    timedOut = true;\r
+                    if (Log.IsDebugEnabled)\r
+                        Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL WITH TIMEOUT", message.Method, message.RequestUri);\r
+                }\r
+                catch(TaskCanceledException exc)\r
+                {\r
+                    //If the task was cancelled due to a timeout, retry it\r
+                    if (!exc.CancellationToken.IsCancellationRequested)\r
+                    {\r
+                        timedOut = true;\r
+                        if (Log.IsDebugEnabled)\r
+                            Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL WITH TIMEOUT", message.Method, message.RequestUri);\r
+                    }\r
+                    else\r
+                    {\r
+                        throw;\r
+                    }\r
+                }\r
                 catch (Exception exc)\r
                 {\r
-                    Log.FatalFormat("Unexpected error while sending:\n{0}\n{1}",message,exc);\r
+                    Log.FatalFormat("Unexpected error while sending:\n{0}\n{1}", message, exc);\r
                     throw;\r
                 }\r
-                \r
+\r
+                if (timedOut)\r
+                {\r
+                    if (--retries == 0)\r
+                        throw new RetryException("Failed too many times");\r
+                    continue;\r
+                }\r
+\r
                 if (result.IsSuccessStatusCode || acceptedCodes.Contains(result.StatusCode))\r
                 {\r
                     if (Log.IsDebugEnabled)\r
-                        Log.DebugFormat("[RESPONSE] [{0}]:[{1}] OK: [{2}]", message.Method,message.RequestUri, result.StatusCode);\r
+                        Log.DebugFormat("[RESPONSE] [{0}]:[{1}] OK: [{2}]", message.Method, message.RequestUri,\r
+                                        result.StatusCode);\r
                     return result;\r
                 }\r
                 //Failed, will have to abort or retry\r
                 if (Log.IsDebugEnabled)\r
-                    Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL: [{2}]\n{3}", message.Method,message.RequestUri, result.StatusCode,result);\r
+                    Log.DebugFormat("[RESPONSE] [{0}]:[{1}] FAIL: [{2}]\n{3}", message.Method, message.RequestUri,\r
+                                    result.StatusCode, result);\r
 \r
                 if (--retries == 0)\r
                     throw new RetryException("Failed too many times");\r
 \r
                 //Wait for service unavailable\r
-                if (result.StatusCode == HttpStatusCode.ServiceUnavailable)\r
+                if (result.StatusCode == HttpStatusCode.ServiceUnavailable ||\r
+                    result.StatusCode == HttpStatusCode.BadGateway)\r
                 {\r
-                    \r
-                    Log.WarnFormat("[UNAVAILABLE] Waiting before retrying [{0}]:[{1}] due to [{2}]",message.Method, message.RequestUri,result.ReasonPhrase);                    \r
+\r
+                    Log.WarnFormat("[UNAVAILABLE] Waiting before retrying [{0}]:[{1}] due to [{2}]", message.Method,\r
+                                   message.RequestUri, result.ReasonPhrase);\r
                     await TaskEx.Delay(waitTime).ConfigureAwait(false);\r
                     //increase the timeout for repeated timeouts\r
-                    if (waitTime<TimeSpan.FromSeconds(10))\r
+                    if (waitTime < TimeSpan.FromSeconds(10))\r
                         waitTime = waitTime.Add(TimeSpan.FromSeconds(10));\r
-                }                \r
-                //Throw in all other cases\r
-                else \r
+                }\r
+                    //Throw in all other cases\r
+                else\r
                     result.EnsureSuccessStatusCode();\r
             }\r
             throw new RetryException();\r
         }\r
 \r
+        public static string GetFirstValue(this HttpResponseHeaders headers, string name)\r
+        {\r
+            if (headers==null)\r
+                throw new ArgumentNullException("headers");\r
+            if (String.IsNullOrWhiteSpace(name))\r
+                throw new ArgumentNullException("name");\r
+            Contract.EndContractBlock();\r
+\r
+            IEnumerable<string> values;\r
+            if (headers.TryGetValues(name, out values))\r
+            {\r
+                return values.FirstOrDefault();\r
+            }\r
+            return null;\r
+        }\r
+\r
+        public static  Dictionary<string, string> GetMeta(this HttpResponseHeaders headers,string metaPrefix)\r
+        {\r
+            if (headers == null)\r
+                throw new ArgumentNullException("headers");\r
+            if (String.IsNullOrWhiteSpace(metaPrefix))\r
+                throw new ArgumentNullException("metaPrefix");\r
+            Contract.EndContractBlock();\r
+\r
+            var dict = (from header in headers\r
+                        where header.Key.StartsWith(metaPrefix)\r
+                        let name = header.Key.Substring(metaPrefix.Length)\r
+                        select new { Name = name, Value = String.Join(",",header.Value) })\r
+                        .ToDictionary(t => t.Name, t => t.Value);\r
+            return dict;\r
+        }\r
+\r
     }\r
 \r
     internal static class PithosEAPCommon\r