Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / RestClient.cs @ 09a3b4e2

History | View | Annotate | Download (20.9 kB)

1 255f5f86 Panagiotis Kanavos
#region
2 255f5f86 Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 255f5f86 Panagiotis Kanavos
 * <copyright file="RestClient.cs" company="GRNet">
4 255f5f86 Panagiotis Kanavos
 * 
5 255f5f86 Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 255f5f86 Panagiotis Kanavos
 *
7 255f5f86 Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 255f5f86 Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 255f5f86 Panagiotis Kanavos
 * conditions are met:
10 255f5f86 Panagiotis Kanavos
 *
11 255f5f86 Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 255f5f86 Panagiotis Kanavos
 *      disclaimer.
14 255f5f86 Panagiotis Kanavos
 *
15 255f5f86 Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 255f5f86 Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 255f5f86 Panagiotis Kanavos
 *      provided with the distribution.
19 255f5f86 Panagiotis Kanavos
 *
20 255f5f86 Panagiotis Kanavos
 *
21 255f5f86 Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 255f5f86 Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 255f5f86 Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 255f5f86 Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 255f5f86 Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 255f5f86 Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 255f5f86 Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 255f5f86 Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 255f5f86 Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 255f5f86 Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 255f5f86 Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 255f5f86 Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 255f5f86 Panagiotis Kanavos
 *
34 255f5f86 Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 255f5f86 Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 255f5f86 Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 255f5f86 Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 255f5f86 Panagiotis Kanavos
 * </copyright>
39 255f5f86 Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 255f5f86 Panagiotis Kanavos
 */
41 255f5f86 Panagiotis Kanavos
#endregion
42 4d301e8e Panagiotis Kanavos
using System.Collections.Specialized;
43 4d301e8e Panagiotis Kanavos
using System.Diagnostics;
44 4d301e8e Panagiotis Kanavos
using System.Diagnostics.Contracts;
45 7d915c34 Panagiotis Kanavos
using System.IO;
46 4d301e8e Panagiotis Kanavos
using System.Net;
47 db8a9589 Panagiotis Kanavos
using System.Reflection;
48 4d301e8e Panagiotis Kanavos
using System.Runtime.Serialization;
49 7d915c34 Panagiotis Kanavos
using System.Threading.Tasks;
50 5120f3cb Panagiotis Kanavos
using log4net;
51 4d301e8e Panagiotis Kanavos
52 27361404 Panagiotis Kanavos
53 4d301e8e Panagiotis Kanavos
namespace Pithos.Network
54 4d301e8e Panagiotis Kanavos
{
55 4d301e8e Panagiotis Kanavos
    using System;
56 4d301e8e Panagiotis Kanavos
    using System.Collections.Generic;
57 4d301e8e Panagiotis Kanavos
    using System.Linq;
58 4d301e8e Panagiotis Kanavos
    using System.Text;
59 4d301e8e Panagiotis Kanavos
60 4d301e8e Panagiotis Kanavos
    /// <summary>
61 4d301e8e Panagiotis Kanavos
    /// TODO: Update summary.
62 4d301e8e Panagiotis Kanavos
    /// </summary>
63 9c4346c9 Panagiotis Kanavos
    public class RestClient:WebClient
64 4d301e8e Panagiotis Kanavos
    {
65 db8a9589 Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
66 db8a9589 Panagiotis Kanavos
67 4d301e8e Panagiotis Kanavos
        public int Timeout { get; set; }
68 4d301e8e Panagiotis Kanavos
69 4d301e8e Panagiotis Kanavos
        public bool TimedOut { get; set; }
70 4d301e8e Panagiotis Kanavos
71 4d301e8e Panagiotis Kanavos
        public HttpStatusCode StatusCode { get; private set; }
72 4d301e8e Panagiotis Kanavos
73 4d301e8e Panagiotis Kanavos
        public string StatusDescription { get; set; }
74 4d301e8e Panagiotis Kanavos
75 a27aa447 Panagiotis Kanavos
        public long? RangeFrom { get; set; }
76 a27aa447 Panagiotis Kanavos
        public long? RangeTo { get; set; }
77 4d301e8e Panagiotis Kanavos
78 4d301e8e Panagiotis Kanavos
        public int Retries { get; set; }
79 4d301e8e Panagiotis Kanavos
80 4d301e8e Panagiotis Kanavos
        private readonly Dictionary<string, string> _parameters=new Dictionary<string, string>();
81 4d301e8e Panagiotis Kanavos
        public Dictionary<string, string> Parameters
82 4d301e8e Panagiotis Kanavos
        {
83 cfed7823 Panagiotis Kanavos
            get
84 cfed7823 Panagiotis Kanavos
            {
85 cfed7823 Panagiotis Kanavos
                Contract.Ensures(_parameters!=null);
86 cfed7823 Panagiotis Kanavos
                return _parameters;
87 cfed7823 Panagiotis Kanavos
            }            
88 cfed7823 Panagiotis Kanavos
        }
89 cfed7823 Panagiotis Kanavos
90 5120f3cb Panagiotis Kanavos
91 cfed7823 Panagiotis Kanavos
        [ContractInvariantMethod]
92 cfed7823 Panagiotis Kanavos
        private void Invariants()
93 cfed7823 Panagiotis Kanavos
        {
94 cfed7823 Panagiotis Kanavos
            Contract.Invariant(Headers!=null);    
95 4d301e8e Panagiotis Kanavos
        }
96 4d301e8e Panagiotis Kanavos
97 9c4346c9 Panagiotis Kanavos
        public RestClient():base()
98 4d301e8e Panagiotis Kanavos
        {
99 72174cd5 pkanavos
            //The maximum error response must be large because missing server hashes are return as a Conflivt (409) error response
100 09a3b4e2 Panagiotis Kanavos
            //Any value above 2^21-1 will result in an empty response.
101 09a3b4e2 Panagiotis Kanavos
            //-1 essentially ignores the maximum length
102 09a3b4e2 Panagiotis Kanavos
            HttpWebRequest.DefaultMaximumErrorResponseLength = -1;
103 4d301e8e Panagiotis Kanavos
        }
104 4d301e8e Panagiotis Kanavos
105 d15e99b4 Panagiotis Kanavos
       
106 9c4346c9 Panagiotis Kanavos
        public RestClient(RestClient other)
107 4d301e8e Panagiotis Kanavos
            : base()
108 4d301e8e Panagiotis Kanavos
        {
109 cfed7823 Panagiotis Kanavos
            if (other==null)
110 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("other");
111 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
112 cfed7823 Panagiotis Kanavos
113 09a3b4e2 Panagiotis Kanavos
            //The maximum error response must be large because missing server hashes are return as a Conflivt (409) error response
114 09a3b4e2 Panagiotis Kanavos
            //Any value above 2^21-1 will result in an empty response.
115 09a3b4e2 Panagiotis Kanavos
            //-1 essentially ignores the maximum length
116 09a3b4e2 Panagiotis Kanavos
            HttpWebRequest.DefaultMaximumErrorResponseLength = -1;
117 5ac32089 pkanavos
118 4d301e8e Panagiotis Kanavos
            CopyHeaders(other);
119 4d301e8e Panagiotis Kanavos
            Timeout = other.Timeout;
120 4d301e8e Panagiotis Kanavos
            Retries = other.Retries;
121 82db721b Panagiotis Kanavos
            BaseAddress = other.BaseAddress;             
122 4d301e8e Panagiotis Kanavos
123 4d301e8e Panagiotis Kanavos
            foreach (var parameter in other.Parameters)
124 4d301e8e Panagiotis Kanavos
            {
125 4d301e8e Panagiotis Kanavos
                Parameters.Add(parameter.Key,parameter.Value);
126 4d301e8e Panagiotis Kanavos
            }
127 4d301e8e Panagiotis Kanavos
128 4d301e8e Panagiotis Kanavos
            this.Proxy = other.Proxy;
129 4d301e8e Panagiotis Kanavos
        }
130 4d301e8e Panagiotis Kanavos
131 06f11e8b Panagiotis Kanavos
132 4d301e8e Panagiotis Kanavos
        protected override WebRequest GetWebRequest(Uri address)
133 4d301e8e Panagiotis Kanavos
        {
134 4d301e8e Panagiotis Kanavos
            TimedOut = false;
135 06f11e8b Panagiotis Kanavos
            var webRequest = base.GetWebRequest(address);            
136 cfed7823 Panagiotis Kanavos
            var request = (HttpWebRequest)webRequest;
137 65282d58 Panagiotis Kanavos
            request.ServicePoint.ConnectionLimit = 50;
138 5d4e820b Panagiotis Kanavos
            if (IfModifiedSince.HasValue)
139 5d4e820b Panagiotis Kanavos
                request.IfModifiedSince = IfModifiedSince.Value;
140 4d301e8e Panagiotis Kanavos
            request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
141 4d301e8e Panagiotis Kanavos
            if(Timeout>0)
142 4d301e8e Panagiotis Kanavos
                request.Timeout = Timeout;
143 a27aa447 Panagiotis Kanavos
144 a27aa447 Panagiotis Kanavos
            if (RangeFrom.HasValue)
145 a27aa447 Panagiotis Kanavos
            {
146 a27aa447 Panagiotis Kanavos
                if (RangeTo.HasValue)
147 a27aa447 Panagiotis Kanavos
                    request.AddRange(RangeFrom.Value, RangeTo.Value);
148 a27aa447 Panagiotis Kanavos
                else
149 a27aa447 Panagiotis Kanavos
                    request.AddRange(RangeFrom.Value);
150 a27aa447 Panagiotis Kanavos
            }
151 4d301e8e Panagiotis Kanavos
            return request; 
152 4d301e8e Panagiotis Kanavos
        }
153 4d301e8e Panagiotis Kanavos
154 5d4e820b Panagiotis Kanavos
        public DateTime? IfModifiedSince { get; set; }
155 5d4e820b Panagiotis Kanavos
156 06f11e8b Panagiotis Kanavos
        //Asynchronous version
157 4d301e8e Panagiotis Kanavos
        protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
158 6bcdd8e2 Panagiotis Kanavos
        {            
159 6bcdd8e2 Panagiotis Kanavos
            Log.InfoFormat("[{0}] {1}", request.Method, request.RequestUri); 
160 06f11e8b Panagiotis Kanavos
            HttpWebResponse response = null;
161 06f11e8b Panagiotis Kanavos
162 06f11e8b Panagiotis Kanavos
            try
163 06f11e8b Panagiotis Kanavos
            {
164 06f11e8b Panagiotis Kanavos
                response = (HttpWebResponse)base.GetWebResponse(request, result);
165 06f11e8b Panagiotis Kanavos
            }
166 06f11e8b Panagiotis Kanavos
            catch (WebException exc)
167 06f11e8b Panagiotis Kanavos
            {
168 6f03d6e1 Panagiotis Kanavos
                if (!TryGetResponse(exc, request,out response))
169 06f11e8b Panagiotis Kanavos
                    throw;
170 06f11e8b Panagiotis Kanavos
            }
171 06f11e8b Panagiotis Kanavos
172 06f11e8b Panagiotis Kanavos
            StatusCode = response.StatusCode;
173 06f11e8b Panagiotis Kanavos
            LastModified = response.LastModified;
174 06f11e8b Panagiotis Kanavos
            StatusDescription = response.StatusDescription;
175 06f11e8b Panagiotis Kanavos
            return response;
176 4d301e8e Panagiotis Kanavos
177 cfed7823 Panagiotis Kanavos
        }
178 06f11e8b Panagiotis Kanavos
      
179 4d301e8e Panagiotis Kanavos
180 06f11e8b Panagiotis Kanavos
        //Synchronous version
181 06f11e8b Panagiotis Kanavos
        protected override WebResponse GetWebResponse(WebRequest request)
182 4d301e8e Panagiotis Kanavos
        {
183 06f11e8b Panagiotis Kanavos
            HttpWebResponse response = null;
184 7d915c34 Panagiotis Kanavos
            try
185 6bcdd8e2 Panagiotis Kanavos
            {           
186 6bcdd8e2 Panagiotis Kanavos
                Log.InfoFormat("[{0}] {1}",request.Method,request.RequestUri);     
187 06f11e8b Panagiotis Kanavos
                response = (HttpWebResponse)base.GetWebResponse(request);
188 7d915c34 Panagiotis Kanavos
            }
189 7d915c34 Panagiotis Kanavos
            catch (WebException exc)
190 5120f3cb Panagiotis Kanavos
            {
191 6f03d6e1 Panagiotis Kanavos
                if (!TryGetResponse(exc, request,out response))
192 06f11e8b Panagiotis Kanavos
                    throw;
193 06f11e8b Panagiotis Kanavos
            }
194 06f11e8b Panagiotis Kanavos
195 06f11e8b Panagiotis Kanavos
            StatusCode = response.StatusCode;
196 06f11e8b Panagiotis Kanavos
            LastModified = response.LastModified;
197 06f11e8b Panagiotis Kanavos
            StatusDescription = response.StatusDescription;
198 06f11e8b Panagiotis Kanavos
            return response;
199 06f11e8b Panagiotis Kanavos
        }
200 06f11e8b Panagiotis Kanavos
201 6f03d6e1 Panagiotis Kanavos
        private bool TryGetResponse(WebException exc, WebRequest request,out HttpWebResponse response)
202 06f11e8b Panagiotis Kanavos
        {
203 06f11e8b Panagiotis Kanavos
            response = null;
204 06f11e8b Panagiotis Kanavos
            //Fail on empty response
205 06f11e8b Panagiotis Kanavos
            if (exc.Response == null)
206 6f03d6e1 Panagiotis Kanavos
            {
207 6f03d6e1 Panagiotis Kanavos
                Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status, request.RequestUri);     
208 06f11e8b Panagiotis Kanavos
                return false;
209 6f03d6e1 Panagiotis Kanavos
            }
210 06f11e8b Panagiotis Kanavos
211 06f11e8b Panagiotis Kanavos
            response = (exc.Response as HttpWebResponse);
212 6f03d6e1 Panagiotis Kanavos
            var statusCode = (int)response.StatusCode;
213 06f11e8b Panagiotis Kanavos
            //Succeed on allowed status codes
214 06f11e8b Panagiotis Kanavos
            if (AllowedStatusCodes.Contains(response.StatusCode))
215 6f03d6e1 Panagiotis Kanavos
            {
216 6f03d6e1 Panagiotis Kanavos
                if (Log.IsDebugEnabled)
217 6f03d6e1 Panagiotis Kanavos
                    Log.DebugFormat("[{0}] {1} {2}", request.Method, statusCode, request.RequestUri);     
218 06f11e8b Panagiotis Kanavos
                return true;
219 6f03d6e1 Panagiotis Kanavos
            }
220 6f03d6e1 Panagiotis Kanavos
            
221 6f03d6e1 Panagiotis Kanavos
            Log.WarnFormat("[{0}] {1} {2}", request.Method, statusCode, request.RequestUri);
222 06f11e8b Panagiotis Kanavos
223 06f11e8b Panagiotis Kanavos
            //Does the response have any content to log?
224 06f11e8b Panagiotis Kanavos
            if (exc.Response.ContentLength > 0)
225 06f11e8b Panagiotis Kanavos
            {
226 692ec33b Panagiotis Kanavos
                var content = LogContent(exc.Response);
227 06f11e8b Panagiotis Kanavos
                Log.ErrorFormat(content);
228 7d915c34 Panagiotis Kanavos
            }
229 06f11e8b Panagiotis Kanavos
            return false;
230 7d915c34 Panagiotis Kanavos
        }
231 7d915c34 Panagiotis Kanavos
232 06f11e8b Panagiotis Kanavos
        private readonly List<HttpStatusCode> _allowedStatusCodes=new List<HttpStatusCode>{HttpStatusCode.NotModified};        
233 06f11e8b Panagiotis Kanavos
234 5120f3cb Panagiotis Kanavos
        public List<HttpStatusCode> AllowedStatusCodes
235 5120f3cb Panagiotis Kanavos
        {
236 5120f3cb Panagiotis Kanavos
            get
237 5120f3cb Panagiotis Kanavos
            {
238 5120f3cb Panagiotis Kanavos
                return _allowedStatusCodes;
239 5120f3cb Panagiotis Kanavos
            }            
240 5120f3cb Panagiotis Kanavos
        }
241 5120f3cb Panagiotis Kanavos
242 bfc13ed8 Panagiotis Kanavos
        public DateTime LastModified { get; private set; }
243 bfc13ed8 Panagiotis Kanavos
244 692ec33b Panagiotis Kanavos
        private static string LogContent(WebResponse webResponse)
245 7d915c34 Panagiotis Kanavos
        {
246 cfed7823 Panagiotis Kanavos
            if (webResponse == null)
247 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("webResponse");
248 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
249 cfed7823 Panagiotis Kanavos
250 692ec33b Panagiotis Kanavos
            //The response stream must be copied to avoid affecting other code by disposing of the 
251 692ec33b Panagiotis Kanavos
            //original response stream.
252 692ec33b Panagiotis Kanavos
            var stream = webResponse.GetResponseStream();            
253 3c76f045 Panagiotis Kanavos
            using(var memStream=new MemoryStream())
254 692ec33b Panagiotis Kanavos
            using (var reader = new StreamReader(memStream))
255 7d915c34 Panagiotis Kanavos
            {
256 692ec33b Panagiotis Kanavos
                stream.CopyTo(memStream);                
257 692ec33b Panagiotis Kanavos
                string content = reader.ReadToEnd();
258 692ec33b Panagiotis Kanavos
259 692ec33b Panagiotis Kanavos
                stream.Seek(0,SeekOrigin.Begin);
260 692ec33b Panagiotis Kanavos
                return content;
261 7d915c34 Panagiotis Kanavos
            }
262 4d301e8e Panagiotis Kanavos
        }
263 4d301e8e Panagiotis Kanavos
264 4d301e8e Panagiotis Kanavos
        public string DownloadStringWithRetry(string address,int retries=0)
265 4d301e8e Panagiotis Kanavos
        {
266 27361404 Panagiotis Kanavos
            
267 4d301e8e Panagiotis Kanavos
            if (address == null)
268 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("address");
269 4d301e8e Panagiotis Kanavos
270 4d301e8e Panagiotis Kanavos
            var actualAddress = GetActualAddress(address);
271 4d301e8e Panagiotis Kanavos
272 82db721b Panagiotis Kanavos
            TraceStart("GET",actualAddress);            
273 82db721b Panagiotis Kanavos
            
274 4d301e8e Panagiotis Kanavos
            var actualRetries = (retries == 0) ? Retries : retries;
275 d15e99b4 Panagiotis Kanavos
276 692ec33b Panagiotis Kanavos
            var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress);
277 692ec33b Panagiotis Kanavos
278 7d915c34 Panagiotis Kanavos
            var task = Retry(() =>
279 692ec33b Panagiotis Kanavos
            {                
280 4d301e8e Panagiotis Kanavos
                var content = base.DownloadString(uriString);
281 4d301e8e Panagiotis Kanavos
282 4d301e8e Panagiotis Kanavos
                if (StatusCode == HttpStatusCode.NoContent)
283 4d301e8e Panagiotis Kanavos
                    return String.Empty;
284 4d301e8e Panagiotis Kanavos
                return content;
285 4d301e8e Panagiotis Kanavos
286 4d301e8e Panagiotis Kanavos
            }, actualRetries);
287 4d301e8e Panagiotis Kanavos
288 65282d58 Panagiotis Kanavos
            try
289 65282d58 Panagiotis Kanavos
            {
290 65282d58 Panagiotis Kanavos
                var result = task.Result;
291 65282d58 Panagiotis Kanavos
                return result;
292 65282d58 Panagiotis Kanavos
293 65282d58 Panagiotis Kanavos
            }
294 65282d58 Panagiotis Kanavos
            catch (AggregateException exc)
295 65282d58 Panagiotis Kanavos
            {
296 65282d58 Panagiotis Kanavos
                //If the task fails, propagate the original exception
297 65282d58 Panagiotis Kanavos
                if (exc.InnerException!=null)
298 65282d58 Panagiotis Kanavos
                    throw exc.InnerException;
299 65282d58 Panagiotis Kanavos
                throw;
300 65282d58 Panagiotis Kanavos
            }
301 4d301e8e Panagiotis Kanavos
        }
302 4d301e8e Panagiotis Kanavos
303 4d301e8e Panagiotis Kanavos
        public void Head(string address,int retries=0)
304 4d301e8e Panagiotis Kanavos
        {
305 cfed7823 Panagiotis Kanavos
            AllowedStatusCodes.Add(HttpStatusCode.NotFound);
306 4d301e8e Panagiotis Kanavos
            RetryWithoutContent(address, retries, "HEAD");
307 4d301e8e Panagiotis Kanavos
        }
308 4d301e8e Panagiotis Kanavos
309 4d301e8e Panagiotis Kanavos
        public void PutWithRetry(string address, int retries = 0)
310 4d301e8e Panagiotis Kanavos
        {
311 4d301e8e Panagiotis Kanavos
            RetryWithoutContent(address, retries, "PUT");
312 4d301e8e Panagiotis Kanavos
        }
313 4d301e8e Panagiotis Kanavos
314 4d301e8e Panagiotis Kanavos
        public void DeleteWithRetry(string address,int retries=0)
315 4d301e8e Panagiotis Kanavos
        {
316 4d301e8e Panagiotis Kanavos
            RetryWithoutContent(address, retries, "DELETE");
317 4d301e8e Panagiotis Kanavos
        }
318 4d301e8e Panagiotis Kanavos
319 aba9e6d9 Panagiotis Kanavos
        public string GetHeaderValue(string headerName,bool optional=false)
320 4d301e8e Panagiotis Kanavos
        {
321 cfed7823 Panagiotis Kanavos
            if (this.ResponseHeaders==null)
322 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("ResponseHeaders are null");
323 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
324 cfed7823 Panagiotis Kanavos
325 4d301e8e Panagiotis Kanavos
            var values=this.ResponseHeaders.GetValues(headerName);
326 aba9e6d9 Panagiotis Kanavos
            if (values != null)
327 4d301e8e Panagiotis Kanavos
                return values[0];
328 aba9e6d9 Panagiotis Kanavos
329 aba9e6d9 Panagiotis Kanavos
            if (optional)            
330 aba9e6d9 Panagiotis Kanavos
                return null;            
331 aba9e6d9 Panagiotis Kanavos
            //A required header was not found
332 aba9e6d9 Panagiotis Kanavos
            throw new WebException(String.Format("The {0}  header is missing", headerName));
333 aba9e6d9 Panagiotis Kanavos
        }
334 aba9e6d9 Panagiotis Kanavos
335 aba9e6d9 Panagiotis Kanavos
        public void SetNonEmptyHeaderValue(string headerName, string value)
336 aba9e6d9 Panagiotis Kanavos
        {
337 aba9e6d9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(value))
338 aba9e6d9 Panagiotis Kanavos
                return;
339 aba9e6d9 Panagiotis Kanavos
            Headers.Add(headerName,value);
340 4d301e8e Panagiotis Kanavos
        }
341 4d301e8e Panagiotis Kanavos
342 4d301e8e Panagiotis Kanavos
        private void RetryWithoutContent(string address, int retries, string method)
343 4d301e8e Panagiotis Kanavos
        {
344 4d301e8e Panagiotis Kanavos
            if (address == null)
345 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("address");
346 4d301e8e Panagiotis Kanavos
347 82db721b Panagiotis Kanavos
            var actualAddress = GetActualAddress(address);            
348 4d301e8e Panagiotis Kanavos
            var actualRetries = (retries == 0) ? Retries : retries;
349 82db721b Panagiotis Kanavos
350 7d915c34 Panagiotis Kanavos
            var task = Retry(() =>
351 4d301e8e Panagiotis Kanavos
            {
352 4d301e8e Panagiotis Kanavos
                var uriString = String.Join("/",BaseAddress ,actualAddress);
353 4d301e8e Panagiotis Kanavos
                var uri = new Uri(uriString);
354 4d301e8e Panagiotis Kanavos
                var request =  GetWebRequest(uri);
355 4d301e8e Panagiotis Kanavos
                request.Method = method;
356 4d301e8e Panagiotis Kanavos
                if (ResponseHeaders!=null)
357 4d301e8e Panagiotis Kanavos
                    ResponseHeaders.Clear();
358 4d301e8e Panagiotis Kanavos
359 82db721b Panagiotis Kanavos
                TraceStart(method, uriString);
360 0af3141d Panagiotis Kanavos
                if (method == "PUT")
361 0af3141d Panagiotis Kanavos
                    request.ContentLength = 0;
362 06f11e8b Panagiotis Kanavos
363 692ec33b Panagiotis Kanavos
                //Have to use try/finally instead of using here, because WebClient needs a valid WebResponse object
364 692ec33b Panagiotis Kanavos
                //in order to return response headers
365 4d301e8e Panagiotis Kanavos
                var response = (HttpWebResponse)GetWebResponse(request);
366 692ec33b Panagiotis Kanavos
                try
367 692ec33b Panagiotis Kanavos
                {
368 692ec33b Panagiotis Kanavos
                    LastModified = response.LastModified;
369 692ec33b Panagiotis Kanavos
                    StatusCode = response.StatusCode;
370 692ec33b Panagiotis Kanavos
                    StatusDescription = response.StatusDescription;
371 692ec33b Panagiotis Kanavos
                }
372 692ec33b Panagiotis Kanavos
                finally
373 692ec33b Panagiotis Kanavos
                {
374 692ec33b Panagiotis Kanavos
                    response.Close();
375 692ec33b Panagiotis Kanavos
                }
376 4d301e8e Panagiotis Kanavos
                
377 4d301e8e Panagiotis Kanavos
378 4d301e8e Panagiotis Kanavos
                return 0;
379 4d301e8e Panagiotis Kanavos
            }, actualRetries);
380 4d301e8e Panagiotis Kanavos
381 5ce54458 Panagiotis Kanavos
            try
382 5ce54458 Panagiotis Kanavos
            {
383 5ce54458 Panagiotis Kanavos
                task.Wait();
384 5ce54458 Panagiotis Kanavos
            }
385 5ce54458 Panagiotis Kanavos
            catch (AggregateException ex)
386 5ce54458 Panagiotis Kanavos
            {
387 5ce54458 Panagiotis Kanavos
                var exc = ex.InnerException;
388 5ce54458 Panagiotis Kanavos
                if (exc is RetryException)
389 5ce54458 Panagiotis Kanavos
                {
390 5120f3cb Panagiotis Kanavos
                    Log.ErrorFormat("[{0}] RETRY FAILED for {1} after {2} retries",method,address,retries);
391 5ce54458 Panagiotis Kanavos
                }
392 5ce54458 Panagiotis Kanavos
                else
393 5ce54458 Panagiotis Kanavos
                {
394 5120f3cb Panagiotis Kanavos
                    Log.ErrorFormat("[{0}] FAILED for {1} with \n{2}", method, address, exc);
395 5ce54458 Panagiotis Kanavos
                }
396 c28a075a Panagiotis Kanavos
                throw exc;
397 5ce54458 Panagiotis Kanavos
398 5ce54458 Panagiotis Kanavos
            }
399 5ce54458 Panagiotis Kanavos
            catch(Exception ex)
400 5ce54458 Panagiotis Kanavos
            {
401 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[{0}] FAILED for {1} with \n{2}", method, address, ex);
402 5ce54458 Panagiotis Kanavos
                throw;
403 5ce54458 Panagiotis Kanavos
            }
404 4d301e8e Panagiotis Kanavos
        }
405 9c4346c9 Panagiotis Kanavos
        
406 82db721b Panagiotis Kanavos
        private static void TraceStart(string method, string actualAddress)
407 82db721b Panagiotis Kanavos
        {
408 5120f3cb Panagiotis Kanavos
            Log.InfoFormat("[{0}] {1} {2}", method, DateTime.Now, actualAddress);
409 82db721b Panagiotis Kanavos
        }
410 82db721b Panagiotis Kanavos
411 4d301e8e Panagiotis Kanavos
        private string GetActualAddress(string address)
412 4d301e8e Panagiotis Kanavos
        {
413 4d301e8e Panagiotis Kanavos
            if (Parameters.Count == 0)
414 4d301e8e Panagiotis Kanavos
                return address;
415 4d301e8e Panagiotis Kanavos
            var addressBuilder=new StringBuilder(address);            
416 4d301e8e Panagiotis Kanavos
417 4d301e8e Panagiotis Kanavos
            bool isFirst = true;
418 4d301e8e Panagiotis Kanavos
            foreach (var parameter in Parameters)
419 4d301e8e Panagiotis Kanavos
            {
420 4d301e8e Panagiotis Kanavos
                if(isFirst)
421 4d301e8e Panagiotis Kanavos
                    addressBuilder.AppendFormat("?{0}={1}", parameter.Key, parameter.Value);
422 4d301e8e Panagiotis Kanavos
                else
423 4d301e8e Panagiotis Kanavos
                    addressBuilder.AppendFormat("&{0}={1}", parameter.Key, parameter.Value);
424 4d301e8e Panagiotis Kanavos
                isFirst = false;
425 4d301e8e Panagiotis Kanavos
            }
426 4d301e8e Panagiotis Kanavos
            return addressBuilder.ToString();
427 4d301e8e Panagiotis Kanavos
        }
428 4d301e8e Panagiotis Kanavos
429 4d301e8e Panagiotis Kanavos
        public string DownloadStringWithRetry(Uri address,int retries=0)
430 4d301e8e Panagiotis Kanavos
        {
431 4d301e8e Panagiotis Kanavos
            if (address == null)
432 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("address");
433 4d301e8e Panagiotis Kanavos
434 4d301e8e Panagiotis Kanavos
            var actualRetries = (retries == 0) ? Retries : retries;            
435 7d915c34 Panagiotis Kanavos
            var task = Retry(() =>
436 4d301e8e Panagiotis Kanavos
            {
437 4d301e8e Panagiotis Kanavos
                var content = base.DownloadString(address);
438 4d301e8e Panagiotis Kanavos
439 4d301e8e Panagiotis Kanavos
                if (StatusCode == HttpStatusCode.NoContent)
440 4d301e8e Panagiotis Kanavos
                    return String.Empty;
441 4d301e8e Panagiotis Kanavos
                return content;
442 4d301e8e Panagiotis Kanavos
443 4d301e8e Panagiotis Kanavos
            }, actualRetries);
444 4d301e8e Panagiotis Kanavos
445 7d915c34 Panagiotis Kanavos
            var result = task.Result;
446 4d301e8e Panagiotis Kanavos
            return result;
447 4d301e8e Panagiotis Kanavos
        }
448 4d301e8e Panagiotis Kanavos
449 d15e99b4 Panagiotis Kanavos
      
450 4d301e8e Panagiotis Kanavos
        /// <summary>
451 9c4346c9 Panagiotis Kanavos
        /// Copies headers from another RestClient
452 4d301e8e Panagiotis Kanavos
        /// </summary>
453 9c4346c9 Panagiotis Kanavos
        /// <param name="source">The RestClient from which the headers are copied</param>
454 9c4346c9 Panagiotis Kanavos
        public void CopyHeaders(RestClient source)
455 4d301e8e Panagiotis Kanavos
        {
456 4d301e8e Panagiotis Kanavos
            if (source == null)
457 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("source", "source can't be null");
458 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
459 cfed7823 Panagiotis Kanavos
            //The Headers getter initializes the property, it is never null
460 cfed7823 Panagiotis Kanavos
            Contract.Assume(Headers!=null);
461 cfed7823 Panagiotis Kanavos
                
462 4d301e8e Panagiotis Kanavos
            CopyHeaders(source.Headers,Headers);
463 4d301e8e Panagiotis Kanavos
        }
464 4d301e8e Panagiotis Kanavos
        
465 4d301e8e Panagiotis Kanavos
        /// <summary>
466 d15e99b4 Panagiotis Kanavos
        /// Copies headers from one header collection to another
467 4d301e8e Panagiotis Kanavos
        /// </summary>
468 d15e99b4 Panagiotis Kanavos
        /// <param name="source">The source collection from which the headers are copied</param>
469 d15e99b4 Panagiotis Kanavos
        /// <param name="target">The target collection to which the headers are copied</param>
470 4d301e8e Panagiotis Kanavos
        public static void CopyHeaders(WebHeaderCollection source,WebHeaderCollection target)
471 4d301e8e Panagiotis Kanavos
        {
472 4d301e8e Panagiotis Kanavos
            if (source == null)
473 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("source", "source can't be null");
474 4d301e8e Panagiotis Kanavos
            if (target == null)
475 4d301e8e Panagiotis Kanavos
                throw new ArgumentNullException("target", "target can't be null");
476 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
477 cfed7823 Panagiotis Kanavos
478 4d301e8e Panagiotis Kanavos
            for (int i = 0; i < source.Count; i++)
479 4d301e8e Panagiotis Kanavos
            {
480 4d301e8e Panagiotis Kanavos
                target.Add(source.GetKey(i), source[i]);
481 4d301e8e Panagiotis Kanavos
            }            
482 4d301e8e Panagiotis Kanavos
        }
483 4d301e8e Panagiotis Kanavos
484 4d301e8e Panagiotis Kanavos
        public void AssertStatusOK(string message)
485 4d301e8e Panagiotis Kanavos
        {
486 4d301e8e Panagiotis Kanavos
            if (StatusCode >= HttpStatusCode.BadRequest)
487 4d301e8e Panagiotis Kanavos
                throw new WebException(String.Format("{0} with code {1} - {2}", message, StatusCode, StatusDescription));
488 4d301e8e Panagiotis Kanavos
        }
489 4d301e8e Panagiotis Kanavos
490 4d301e8e Panagiotis Kanavos
491 0c02aa65 Panagiotis Kanavos
        private Task<T> Retry<T>(Func<T> original, int retryCount, TaskCompletionSource<T> tcs = null)
492 7d915c34 Panagiotis Kanavos
        {
493 cfed7823 Panagiotis Kanavos
            if (original==null)
494 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("original");
495 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
496 cfed7823 Panagiotis Kanavos
497 0c02aa65 Panagiotis Kanavos
            if (tcs == null)
498 0c02aa65 Panagiotis Kanavos
                tcs = new TaskCompletionSource<T>();
499 0c02aa65 Panagiotis Kanavos
            Task.Factory.StartNew(original).ContinueWith(_original =>
500 7d915c34 Panagiotis Kanavos
                {
501 0c02aa65 Panagiotis Kanavos
                    if (!_original.IsFaulted)
502 0c02aa65 Panagiotis Kanavos
                        tcs.SetFromTask(_original);
503 0c02aa65 Panagiotis Kanavos
                    else 
504 7d915c34 Panagiotis Kanavos
                    {
505 7d915c34 Panagiotis Kanavos
                        var e = _original.Exception.InnerException;
506 0c02aa65 Panagiotis Kanavos
                        var we = (e as WebException);
507 0c02aa65 Panagiotis Kanavos
                        if (we==null)
508 0c02aa65 Panagiotis Kanavos
                            tcs.SetException(e);
509 0c02aa65 Panagiotis Kanavos
                        else
510 7d915c34 Panagiotis Kanavos
                        {
511 0c02aa65 Panagiotis Kanavos
                            var statusCode = GetStatusCode(we);
512 0c02aa65 Panagiotis Kanavos
513 0c02aa65 Panagiotis Kanavos
                            //Return null for 404
514 0c02aa65 Panagiotis Kanavos
                            if (statusCode == HttpStatusCode.NotFound)
515 0c02aa65 Panagiotis Kanavos
                                tcs.SetResult(default(T));
516 0c02aa65 Panagiotis Kanavos
                            //Retry for timeouts and service unavailable
517 0c02aa65 Panagiotis Kanavos
                            else if (we.Status == WebExceptionStatus.Timeout ||
518 0c02aa65 Panagiotis Kanavos
                                (we.Status == WebExceptionStatus.ProtocolError && statusCode == HttpStatusCode.ServiceUnavailable))
519 7d915c34 Panagiotis Kanavos
                            {
520 7d915c34 Panagiotis Kanavos
                                TimedOut = true;
521 7d915c34 Panagiotis Kanavos
                                if (retryCount == 0)
522 5ce54458 Panagiotis Kanavos
                                {                                    
523 5120f3cb Panagiotis Kanavos
                                    Log.ErrorFormat("[ERROR] Timed out too many times. \n{0}\n",e);
524 0c02aa65 Panagiotis Kanavos
                                    tcs.SetException(new RetryException("Timed out too many times.", e));                                    
525 0c02aa65 Panagiotis Kanavos
                                }
526 0c02aa65 Panagiotis Kanavos
                                else
527 0c02aa65 Panagiotis Kanavos
                                {
528 5120f3cb Panagiotis Kanavos
                                    Log.ErrorFormat(
529 0c02aa65 Panagiotis Kanavos
                                        "[RETRY] Timed out after {0} ms. Will retry {1} more times\n{2}", Timeout,
530 0c02aa65 Panagiotis Kanavos
                                        retryCount, e);
531 0c02aa65 Panagiotis Kanavos
                                    Retry(original, retryCount - 1, tcs);
532 7d915c34 Panagiotis Kanavos
                                }
533 7d915c34 Panagiotis Kanavos
                            }
534 0c02aa65 Panagiotis Kanavos
                            else
535 0c02aa65 Panagiotis Kanavos
                                tcs.SetException(e);
536 7d915c34 Panagiotis Kanavos
                        }
537 0c02aa65 Panagiotis Kanavos
                    };
538 0c02aa65 Panagiotis Kanavos
                });
539 0c02aa65 Panagiotis Kanavos
            return tcs.Task;
540 4d301e8e Panagiotis Kanavos
        }
541 4d301e8e Panagiotis Kanavos
542 0c02aa65 Panagiotis Kanavos
        private HttpStatusCode GetStatusCode(WebException we)
543 0c02aa65 Panagiotis Kanavos
        {
544 cfed7823 Panagiotis Kanavos
            if (we==null)
545 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("we");
546 0c02aa65 Panagiotis Kanavos
            var statusCode = HttpStatusCode.RequestTimeout;
547 0c02aa65 Panagiotis Kanavos
            if (we.Response != null)
548 0c02aa65 Panagiotis Kanavos
            {
549 0c02aa65 Panagiotis Kanavos
                statusCode = ((HttpWebResponse) we.Response).StatusCode;
550 0c02aa65 Panagiotis Kanavos
                this.StatusCode = statusCode;
551 0c02aa65 Panagiotis Kanavos
            }
552 0c02aa65 Panagiotis Kanavos
            return statusCode;
553 0c02aa65 Panagiotis Kanavos
        }
554 cfed7823 Panagiotis Kanavos
555 cfed7823 Panagiotis Kanavos
        public UriBuilder GetAddressBuilder(string container, string objectName)
556 cfed7823 Panagiotis Kanavos
        {
557 cfed7823 Panagiotis Kanavos
            var builder = new UriBuilder(String.Join("/", BaseAddress, container, objectName));
558 cfed7823 Panagiotis Kanavos
            return builder;
559 cfed7823 Panagiotis Kanavos
        }
560 c92e02f3 Panagiotis Kanavos
561 c92e02f3 Panagiotis Kanavos
        public Dictionary<string, string> GetMeta(string metaPrefix)
562 c92e02f3 Panagiotis Kanavos
        {
563 c92e02f3 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(metaPrefix))
564 c92e02f3 Panagiotis Kanavos
                throw new ArgumentNullException("metaPrefix");
565 c92e02f3 Panagiotis Kanavos
            Contract.EndContractBlock();
566 c92e02f3 Panagiotis Kanavos
567 c92e02f3 Panagiotis Kanavos
            var keys = ResponseHeaders.AllKeys.AsQueryable();
568 c92e02f3 Panagiotis Kanavos
            var dict = (from key in keys
569 c92e02f3 Panagiotis Kanavos
                        where key.StartsWith(metaPrefix)
570 c92e02f3 Panagiotis Kanavos
                        let name = key.Substring(metaPrefix.Length)
571 c92e02f3 Panagiotis Kanavos
                        select new { Name = name, Value = ResponseHeaders[key] })
572 c92e02f3 Panagiotis Kanavos
                        .ToDictionary(t => t.Name, t => t.Value);
573 c92e02f3 Panagiotis Kanavos
            return dict;
574 c92e02f3 Panagiotis Kanavos
        }
575 4d301e8e Panagiotis Kanavos
    }
576 4d301e8e Panagiotis Kanavos
577 4d301e8e Panagiotis Kanavos
    public class RetryException:Exception
578 4d301e8e Panagiotis Kanavos
    {
579 4d301e8e Panagiotis Kanavos
        public RetryException()
580 4d301e8e Panagiotis Kanavos
            :base()
581 4d301e8e Panagiotis Kanavos
        {
582 4d301e8e Panagiotis Kanavos
            
583 4d301e8e Panagiotis Kanavos
        }
584 4d301e8e Panagiotis Kanavos
585 4d301e8e Panagiotis Kanavos
        public RetryException(string message)
586 4d301e8e Panagiotis Kanavos
            :base(message)
587 4d301e8e Panagiotis Kanavos
        {
588 4d301e8e Panagiotis Kanavos
            
589 4d301e8e Panagiotis Kanavos
        }
590 4d301e8e Panagiotis Kanavos
591 4d301e8e Panagiotis Kanavos
        public RetryException(string message,Exception innerException)
592 4d301e8e Panagiotis Kanavos
            :base(message,innerException)
593 4d301e8e Panagiotis Kanavos
        {
594 4d301e8e Panagiotis Kanavos
            
595 4d301e8e Panagiotis Kanavos
        }
596 4d301e8e Panagiotis Kanavos
597 4d301e8e Panagiotis Kanavos
        public RetryException(SerializationInfo info,StreamingContext context)
598 4d301e8e Panagiotis Kanavos
            :base(info,context)
599 4d301e8e Panagiotis Kanavos
        {
600 4d301e8e Panagiotis Kanavos
            
601 4d301e8e Panagiotis Kanavos
        }
602 4d301e8e Panagiotis Kanavos
    }
603 4d301e8e Panagiotis Kanavos
}