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
using System.Threading;\r
using System.Threading.Tasks;\r
using log4net;\r
+using System.ServiceModel.Channels;\r
\r
namespace Pithos.Network\r
{\r
\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
\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