Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / CloudFilesClient.cs @ 42800be8

History | View | Annotate | Download (48.8 kB)

1 5ce54458 Panagiotis Kanavos
// **CloudFilesClient** provides a simple client interface to CloudFiles and Pithos
2 5ce54458 Panagiotis Kanavos
//
3 5ce54458 Panagiotis Kanavos
// The class provides methods to upload/download files, delete files, manage containers
4 5ce54458 Panagiotis Kanavos
5 5ce54458 Panagiotis Kanavos
6 5ce54458 Panagiotis Kanavos
using System;
7 d78cbf09 Panagiotis Kanavos
using System.Collections.Generic;
8 d78cbf09 Panagiotis Kanavos
using System.ComponentModel.Composition;
9 d78cbf09 Panagiotis Kanavos
using System.Diagnostics.Contracts;
10 d78cbf09 Panagiotis Kanavos
using System.IO;
11 d78cbf09 Panagiotis Kanavos
using System.Linq;
12 d78cbf09 Panagiotis Kanavos
using System.Net;
13 d78cbf09 Panagiotis Kanavos
using System.Security.Cryptography;
14 d78cbf09 Panagiotis Kanavos
using System.Text;
15 b5061ac8 Panagiotis Kanavos
using System.Threading.Tasks;
16 d78cbf09 Panagiotis Kanavos
using Newtonsoft.Json;
17 d78cbf09 Panagiotis Kanavos
using Pithos.Interfaces;
18 5120f3cb Panagiotis Kanavos
using log4net;
19 d78cbf09 Panagiotis Kanavos
20 d78cbf09 Panagiotis Kanavos
namespace Pithos.Network
21 d78cbf09 Panagiotis Kanavos
{
22 d78cbf09 Panagiotis Kanavos
    [Export(typeof(ICloudClient))]
23 d78cbf09 Panagiotis Kanavos
    public class CloudFilesClient:ICloudClient
24 d78cbf09 Panagiotis Kanavos
    {
25 5ce54458 Panagiotis Kanavos
        //CloudFilesClient uses *_baseClient* internally to communicate with the server
26 5ce54458 Panagiotis Kanavos
        //RestClient provides a REST-friendly interface over the standard WebClient.
27 9c4346c9 Panagiotis Kanavos
        private RestClient _baseClient;
28 5ce54458 Panagiotis Kanavos
        
29 c53aa229 Panagiotis Kanavos
30 5ce54458 Panagiotis Kanavos
        //During authentication the client provides a UserName 
31 d78cbf09 Panagiotis Kanavos
        public string UserName { get; set; }
32 5ce54458 Panagiotis Kanavos
        
33 5ce54458 Panagiotis Kanavos
        //and and ApiKey to the server
34 5ce54458 Panagiotis Kanavos
        public string ApiKey { get; set; }
35 5ce54458 Panagiotis Kanavos
        
36 5ce54458 Panagiotis Kanavos
        //And receives an authentication Token. This token must be provided in ALL other operations,
37 5ce54458 Panagiotis Kanavos
        //in the X-Auth-Token header
38 c53aa229 Panagiotis Kanavos
        private string _token;
39 c53aa229 Panagiotis Kanavos
        public string Token
40 c53aa229 Panagiotis Kanavos
        {
41 c53aa229 Panagiotis Kanavos
            get { return _token; }
42 c53aa229 Panagiotis Kanavos
            set
43 c53aa229 Panagiotis Kanavos
            {
44 c53aa229 Panagiotis Kanavos
                _token = value;
45 c53aa229 Panagiotis Kanavos
                _baseClient.Headers["X-Auth-Token"] = value;
46 c53aa229 Panagiotis Kanavos
            }
47 c53aa229 Panagiotis Kanavos
        }
48 c53aa229 Panagiotis Kanavos
49 5ce54458 Panagiotis Kanavos
        //The client also receives a StorageUrl after authentication. All subsequent operations must
50 5ce54458 Panagiotis Kanavos
        //use this url
51 5ce54458 Panagiotis Kanavos
        public Uri StorageUrl { get; set; }
52 cfed7823 Panagiotis Kanavos
53 c53aa229 Panagiotis Kanavos
54 cfed7823 Panagiotis Kanavos
        protected Uri RootAddressUri { get; set; }
55 cfed7823 Panagiotis Kanavos
56 c53aa229 Panagiotis Kanavos
        private Uri _proxy;
57 c53aa229 Panagiotis Kanavos
        public Uri Proxy
58 c53aa229 Panagiotis Kanavos
        {
59 c53aa229 Panagiotis Kanavos
            get { return _proxy; }
60 c53aa229 Panagiotis Kanavos
            set
61 c53aa229 Panagiotis Kanavos
            {
62 c53aa229 Panagiotis Kanavos
                _proxy = value;
63 c53aa229 Panagiotis Kanavos
                if (_baseClient != null)
64 c53aa229 Panagiotis Kanavos
                    _baseClient.Proxy = new WebProxy(value);                
65 c53aa229 Panagiotis Kanavos
            }
66 c53aa229 Panagiotis Kanavos
        }
67 b5061ac8 Panagiotis Kanavos
68 b5061ac8 Panagiotis Kanavos
        public double DownloadPercentLimit { get; set; }
69 b5061ac8 Panagiotis Kanavos
        public double UploadPercentLimit { get; set; }
70 79736291 Panagiotis Kanavos
71 79736291 Panagiotis Kanavos
        public string AuthenticationUrl { get; set; }
72 79736291 Panagiotis Kanavos
73 1482f9ad Panagiotis Kanavos
 
74 1482f9ad Panagiotis Kanavos
        public string VersionPath
75 1482f9ad Panagiotis Kanavos
        {
76 1482f9ad Panagiotis Kanavos
            get { return UsePithos ? "v1" : "v1.0"; }
77 1482f9ad Panagiotis Kanavos
        }
78 2c053915 Panagiotis Kanavos
79 2c053915 Panagiotis Kanavos
        public bool UsePithos { get; set; }
80 d78cbf09 Panagiotis Kanavos
81 3c43ec9b Panagiotis Kanavos
82 5120f3cb Panagiotis Kanavos
        private static readonly ILog Log = LogManager.GetLogger("CloudFilesClient");
83 5120f3cb Panagiotis Kanavos
84 c53aa229 Panagiotis Kanavos
        public CloudFilesClient(string userName, string apiKey)
85 d78cbf09 Panagiotis Kanavos
        {
86 c53aa229 Panagiotis Kanavos
            UserName = userName;
87 c53aa229 Panagiotis Kanavos
            ApiKey = apiKey;
88 c53aa229 Panagiotis Kanavos
        }
89 c53aa229 Panagiotis Kanavos
90 c53aa229 Panagiotis Kanavos
        public CloudFilesClient(AccountInfo accountInfo)
91 c53aa229 Panagiotis Kanavos
        {
92 c53aa229 Panagiotis Kanavos
            if (accountInfo==null)
93 c53aa229 Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
94 c53aa229 Panagiotis Kanavos
            Contract.Ensures(!String.IsNullOrWhiteSpace(Token));
95 c53aa229 Panagiotis Kanavos
            Contract.Ensures(StorageUrl != null);
96 cfed7823 Panagiotis Kanavos
            Contract.Ensures(_baseClient != null);
97 c53aa229 Panagiotis Kanavos
            Contract.Ensures(RootAddressUri != null);
98 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
99 cfed7823 Panagiotis Kanavos
100 c53aa229 Panagiotis Kanavos
            _baseClient = new RestClient
101 c53aa229 Panagiotis Kanavos
            {
102 c53aa229 Panagiotis Kanavos
                BaseAddress = accountInfo.StorageUri.ToString(),
103 c53aa229 Panagiotis Kanavos
                Timeout = 10000,
104 c53aa229 Panagiotis Kanavos
                Retries = 3
105 c53aa229 Panagiotis Kanavos
            };
106 c53aa229 Panagiotis Kanavos
            StorageUrl = accountInfo.StorageUri;
107 c53aa229 Panagiotis Kanavos
            Token = accountInfo.Token;
108 c53aa229 Panagiotis Kanavos
            UserName = accountInfo.UserName;
109 c53aa229 Panagiotis Kanavos
110 c53aa229 Panagiotis Kanavos
            //Get the root address (StorageUrl without the account)
111 c53aa229 Panagiotis Kanavos
            var storageUrl = StorageUrl.AbsoluteUri;
112 c53aa229 Panagiotis Kanavos
            var usernameIndex = storageUrl.LastIndexOf(UserName);
113 c53aa229 Panagiotis Kanavos
            var rootUrl = storageUrl.Substring(0, usernameIndex);
114 c53aa229 Panagiotis Kanavos
            RootAddressUri = new Uri(rootUrl);
115 c53aa229 Panagiotis Kanavos
        }
116 d78cbf09 Panagiotis Kanavos
117 3c43ec9b Panagiotis Kanavos
118 c53aa229 Panagiotis Kanavos
        public AccountInfo Authenticate()
119 c53aa229 Panagiotis Kanavos
        {
120 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(UserName))
121 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("UserName is empty");
122 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(ApiKey))
123 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("ApiKey is empty");
124 c53aa229 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(AuthenticationUrl))
125 c53aa229 Panagiotis Kanavos
                throw new InvalidOperationException("AuthenticationUrl is empty");
126 c53aa229 Panagiotis Kanavos
            Contract.Ensures(!String.IsNullOrWhiteSpace(Token));
127 c53aa229 Panagiotis Kanavos
            Contract.Ensures(StorageUrl != null);
128 c53aa229 Panagiotis Kanavos
            Contract.Ensures(_baseClient != null);
129 c53aa229 Panagiotis Kanavos
            Contract.Ensures(RootAddressUri != null);
130 c53aa229 Panagiotis Kanavos
            Contract.EndContractBlock();
131 c53aa229 Panagiotis Kanavos
132 c53aa229 Panagiotis Kanavos
133 c53aa229 Panagiotis Kanavos
            Log.InfoFormat("[AUTHENTICATE] Start for {0}", UserName);
134 d78cbf09 Panagiotis Kanavos
135 9c4346c9 Panagiotis Kanavos
            using (var authClient = new RestClient{BaseAddress=AuthenticationUrl})
136 cd7c162f Panagiotis Kanavos
            {
137 4d301e8e Panagiotis Kanavos
                if (Proxy != null)
138 4d301e8e Panagiotis Kanavos
                    authClient.Proxy = new WebProxy(Proxy);
139 d78cbf09 Panagiotis Kanavos
140 cfed7823 Panagiotis Kanavos
                Contract.Assume(authClient.Headers!=null);
141 cfed7823 Panagiotis Kanavos
142 4d301e8e Panagiotis Kanavos
                authClient.Headers.Add("X-Auth-User", UserName);
143 4d301e8e Panagiotis Kanavos
                authClient.Headers.Add("X-Auth-Key", ApiKey);
144 0eea575a Panagiotis Kanavos
145 cd7c162f Panagiotis Kanavos
                authClient.DownloadStringWithRetry(VersionPath, 3);
146 0eea575a Panagiotis Kanavos
147 4d301e8e Panagiotis Kanavos
                authClient.AssertStatusOK("Authentication failed");
148 0eea575a Panagiotis Kanavos
149 cd7c162f Panagiotis Kanavos
                var storageUrl = authClient.GetHeaderValue("X-Storage-Url");
150 0eea575a Panagiotis Kanavos
                if (String.IsNullOrWhiteSpace(storageUrl))
151 0eea575a Panagiotis Kanavos
                    throw new InvalidOperationException("Failed to obtain storage url");
152 c53aa229 Panagiotis Kanavos
                
153 c53aa229 Panagiotis Kanavos
                _baseClient = new RestClient
154 c53aa229 Panagiotis Kanavos
                {
155 c53aa229 Panagiotis Kanavos
                    BaseAddress = storageUrl,
156 c53aa229 Panagiotis Kanavos
                    Timeout = 10000,
157 c53aa229 Panagiotis Kanavos
                    Retries = 3
158 c53aa229 Panagiotis Kanavos
                };
159 c53aa229 Panagiotis Kanavos
160 0eea575a Panagiotis Kanavos
                StorageUrl = new Uri(storageUrl);
161 d9643dd6 Panagiotis Kanavos
                
162 cfed7823 Panagiotis Kanavos
                //Get the root address (StorageUrl without the account)
163 cfed7823 Panagiotis Kanavos
                var usernameIndex=storageUrl.LastIndexOf(UserName);
164 cfed7823 Panagiotis Kanavos
                var rootUrl = storageUrl.Substring(0, usernameIndex);
165 cfed7823 Panagiotis Kanavos
                RootAddressUri = new Uri(rootUrl);
166 cfed7823 Panagiotis Kanavos
                
167 4d301e8e Panagiotis Kanavos
                var token = authClient.GetHeaderValue("X-Auth-Token");
168 b6c72f62 Panagiotis Kanavos
                if (String.IsNullOrWhiteSpace(token))
169 b6c72f62 Panagiotis Kanavos
                    throw new InvalidOperationException("Failed to obtain token url");
170 b6c72f62 Panagiotis Kanavos
                Token = token;
171 c53aa229 Panagiotis Kanavos
172 b6c72f62 Panagiotis Kanavos
            }
173 d78cbf09 Panagiotis Kanavos
174 c53aa229 Panagiotis Kanavos
            Log.InfoFormat("[AUTHENTICATE] End for {0}", UserName);
175 0bd56b7c Panagiotis Kanavos
            
176 9c4346c9 Panagiotis Kanavos
177 c53aa229 Panagiotis Kanavos
            return new AccountInfo {StorageUri = StorageUrl, Token = Token, UserName = UserName};            
178 2c053915 Panagiotis Kanavos
179 b5061ac8 Panagiotis Kanavos
        }
180 d78cbf09 Panagiotis Kanavos
181 d78cbf09 Panagiotis Kanavos
182 cfed7823 Panagiotis Kanavos
183 cfed7823 Panagiotis Kanavos
        public IList<ContainerInfo> ListContainers(string account)
184 9c4346c9 Panagiotis Kanavos
        {
185 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
186 9c4346c9 Panagiotis Kanavos
            {
187 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
188 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
189 cfed7823 Panagiotis Kanavos
                
190 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
191 9c4346c9 Panagiotis Kanavos
                client.Parameters.Add("format", "json");
192 5ce54458 Panagiotis Kanavos
                var content = client.DownloadStringWithRetry("", 3);
193 9c4346c9 Panagiotis Kanavos
                client.AssertStatusOK("List Containers failed");
194 9c4346c9 Panagiotis Kanavos
195 9c4346c9 Panagiotis Kanavos
                if (client.StatusCode == HttpStatusCode.NoContent)
196 9c4346c9 Panagiotis Kanavos
                    return new List<ContainerInfo>();
197 9c4346c9 Panagiotis Kanavos
                var infos = JsonConvert.DeserializeObject<IList<ContainerInfo>>(content);
198 5750d7cc Panagiotis Kanavos
                
199 5750d7cc Panagiotis Kanavos
                foreach (var info in infos)
200 5750d7cc Panagiotis Kanavos
                {
201 5750d7cc Panagiotis Kanavos
                    info.Account = account;
202 5750d7cc Panagiotis Kanavos
                }
203 9c4346c9 Panagiotis Kanavos
                return infos;
204 9c4346c9 Panagiotis Kanavos
            }
205 4d301e8e Panagiotis Kanavos
206 d78cbf09 Panagiotis Kanavos
        }
207 d78cbf09 Panagiotis Kanavos
208 cfed7823 Panagiotis Kanavos
        private string GetAccountUrl(string account)
209 cfed7823 Panagiotis Kanavos
        {
210 cfed7823 Panagiotis Kanavos
            return new Uri(this.RootAddressUri, new Uri(account,UriKind.Relative)).AbsoluteUri;
211 cfed7823 Panagiotis Kanavos
        }
212 cfed7823 Panagiotis Kanavos
213 cfed7823 Panagiotis Kanavos
        public IList<ShareAccountInfo> ListSharingAccounts(DateTime? since=null)
214 cfed7823 Panagiotis Kanavos
        {
215 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("List Accounts"))
216 cfed7823 Panagiotis Kanavos
            {
217 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
218 cfed7823 Panagiotis Kanavos
219 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
220 5120f3cb Panagiotis Kanavos
                {
221 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
222 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
223 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
224 cfed7823 Panagiotis Kanavos
225 5120f3cb Panagiotis Kanavos
                    //Extract the username from the base address
226 5120f3cb Panagiotis Kanavos
                    client.BaseAddress = RootAddressUri.AbsoluteUri;
227 cfed7823 Panagiotis Kanavos
228 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(@"", 3);
229 cfed7823 Panagiotis Kanavos
230 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListSharingAccounts failed");
231 5120f3cb Panagiotis Kanavos
232 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
233 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
234 5120f3cb Panagiotis Kanavos
                                    ? new List<ShareAccountInfo>()
235 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the account list into a list of ShareAccountInfos
236 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ShareAccountInfo>>(content);
237 5120f3cb Panagiotis Kanavos
238 5120f3cb Panagiotis Kanavos
                    Log.DebugFormat("END");
239 5120f3cb Panagiotis Kanavos
                    return infos;
240 5120f3cb Panagiotis Kanavos
                }
241 cfed7823 Panagiotis Kanavos
            }
242 cfed7823 Panagiotis Kanavos
        }
243 cfed7823 Panagiotis Kanavos
244 5ce54458 Panagiotis Kanavos
        //Request listing of all objects in a container modified since a specific time.
245 5ce54458 Panagiotis Kanavos
        //If the *since* value is missing, return all objects
246 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListSharedObjects(DateTime? since = null)
247 cfed7823 Panagiotis Kanavos
        {
248 cfed7823 Panagiotis Kanavos
249 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("List Objects"))
250 cfed7823 Panagiotis Kanavos
            {
251 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
252 5120f3cb Panagiotis Kanavos
253 5120f3cb Panagiotis Kanavos
                var objects = new List<ObjectInfo>();
254 5120f3cb Panagiotis Kanavos
                var accounts = ListSharingAccounts(since);
255 5120f3cb Panagiotis Kanavos
                foreach (var account in accounts)
256 cfed7823 Panagiotis Kanavos
                {
257 5120f3cb Panagiotis Kanavos
                    var containers = ListContainers(account.name);
258 5120f3cb Panagiotis Kanavos
                    foreach (var container in containers)
259 5120f3cb Panagiotis Kanavos
                    {
260 0bd56b7c Panagiotis Kanavos
                        var containerObjects = ListObjects(account.name, container.Name, null);
261 5120f3cb Panagiotis Kanavos
                        objects.AddRange(containerObjects);
262 5120f3cb Panagiotis Kanavos
                    }
263 cfed7823 Panagiotis Kanavos
                }
264 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("END");
265 5120f3cb Panagiotis Kanavos
                return objects;
266 cfed7823 Panagiotis Kanavos
            }
267 cfed7823 Panagiotis Kanavos
        }
268 cfed7823 Panagiotis Kanavos
269 cfed7823 Panagiotis Kanavos
        public void ShareObject(string account, string container, string objectName, string shareTo, bool read, bool write)
270 cfed7823 Panagiotis Kanavos
        {
271 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
272 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("The Token is not set");
273 cfed7823 Panagiotis Kanavos
            if (StorageUrl==null)
274 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("The StorageUrl is not set");
275 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
276 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("container");
277 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
278 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
279 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(account))
280 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("account");
281 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(shareTo))
282 cfed7823 Panagiotis Kanavos
                throw new ArgumentNullException("shareTo");
283 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
284 cfed7823 Panagiotis Kanavos
285 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Share"].Push("Share Object"))
286 cfed7823 Panagiotis Kanavos
            {
287 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
288 5120f3cb Panagiotis Kanavos
                
289 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
290 5120f3cb Panagiotis Kanavos
                {
291 cfed7823 Panagiotis Kanavos
292 5120f3cb Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
293 cfed7823 Panagiotis Kanavos
294 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
295 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
296 5120f3cb Panagiotis Kanavos
297 5120f3cb Panagiotis Kanavos
                    string permission = "";
298 5120f3cb Panagiotis Kanavos
                    if (write)
299 5120f3cb Panagiotis Kanavos
                        permission = String.Format("write={0}", shareTo);
300 5120f3cb Panagiotis Kanavos
                    else if (read)
301 5120f3cb Panagiotis Kanavos
                        permission = String.Format("read={0}", shareTo);
302 5120f3cb Panagiotis Kanavos
                    client.Headers.Add("X-Object-Sharing", permission);
303 cfed7823 Panagiotis Kanavos
304 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
305 cfed7823 Panagiotis Kanavos
306 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ShareObject failed");
307 cfed7823 Panagiotis Kanavos
308 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
309 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
310 5120f3cb Panagiotis Kanavos
                                    ? new List<ObjectInfo>()
311 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the object list into a list of ObjectInfos
312 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
313 5120f3cb Panagiotis Kanavos
314 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
315 5120f3cb Panagiotis Kanavos
                }
316 cfed7823 Panagiotis Kanavos
            }
317 cfed7823 Panagiotis Kanavos
318 5120f3cb Panagiotis Kanavos
319 cfed7823 Panagiotis Kanavos
        }
320 cfed7823 Panagiotis Kanavos
321 0bd56b7c Panagiotis Kanavos
        public AccountInfo GetAccountPolicies(AccountInfo accountInfo)
322 0bd56b7c Panagiotis Kanavos
        {
323 0bd56b7c Panagiotis Kanavos
            if (accountInfo==null)
324 0bd56b7c Panagiotis Kanavos
                throw new ArgumentNullException("accountInfo");
325 0bd56b7c Panagiotis Kanavos
            Contract.EndContractBlock();
326 0bd56b7c Panagiotis Kanavos
327 0bd56b7c Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Account"].Push("GetPolicies"))
328 0bd56b7c Panagiotis Kanavos
            {
329 0bd56b7c Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
330 0bd56b7c Panagiotis Kanavos
331 0bd56b7c Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
332 0bd56b7c Panagiotis Kanavos
                {
333 0bd56b7c Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(accountInfo.UserName))
334 0bd56b7c Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(accountInfo.UserName);
335 0bd56b7c Panagiotis Kanavos
336 0bd56b7c Panagiotis Kanavos
                    client.Parameters.Clear();
337 0bd56b7c Panagiotis Kanavos
                    client.Parameters.Add("format", "json");                    
338 0bd56b7c Panagiotis Kanavos
                    client.Head(String.Empty, 3);
339 0bd56b7c Panagiotis Kanavos
340 0bd56b7c Panagiotis Kanavos
                    var quotaValue=client.ResponseHeaders["X-Account-Policy-Quota"];
341 0bd56b7c Panagiotis Kanavos
                    var bytesValue= client.ResponseHeaders["X-Account-Bytes-Used"];
342 0bd56b7c Panagiotis Kanavos
343 0bd56b7c Panagiotis Kanavos
                    long quota, bytes;
344 0bd56b7c Panagiotis Kanavos
                    if (long.TryParse(quotaValue, out quota))
345 0bd56b7c Panagiotis Kanavos
                        accountInfo.Quota = quota;
346 0bd56b7c Panagiotis Kanavos
                    if (long.TryParse(bytesValue, out bytes))
347 0bd56b7c Panagiotis Kanavos
                        accountInfo.BytesUsed = bytes;
348 0bd56b7c Panagiotis Kanavos
                    
349 0bd56b7c Panagiotis Kanavos
                    return accountInfo;
350 0bd56b7c Panagiotis Kanavos
351 0bd56b7c Panagiotis Kanavos
                }
352 0bd56b7c Panagiotis Kanavos
353 0bd56b7c Panagiotis Kanavos
            }
354 0bd56b7c Panagiotis Kanavos
        }
355 0bd56b7c Panagiotis Kanavos
356 cfed7823 Panagiotis Kanavos
357 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null)
358 d78cbf09 Panagiotis Kanavos
        {
359 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
360 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("container");
361 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
362 d78cbf09 Panagiotis Kanavos
363 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("List"))
364 9c4346c9 Panagiotis Kanavos
            {
365 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
366 cfed7823 Panagiotis Kanavos
367 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
368 5120f3cb Panagiotis Kanavos
                {
369 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
370 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
371 4d301e8e Panagiotis Kanavos
372 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
373 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
374 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
375 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
376 4d301e8e Panagiotis Kanavos
377 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListObjects failed");
378 d78cbf09 Panagiotis Kanavos
379 5120f3cb Panagiotis Kanavos
                    //If the result is empty, return an empty list,
380 5120f3cb Panagiotis Kanavos
                    var infos = String.IsNullOrWhiteSpace(content)
381 5120f3cb Panagiotis Kanavos
                                    ? new List<ObjectInfo>()
382 5120f3cb Panagiotis Kanavos
                                //Otherwise deserialize the object list into a list of ObjectInfos
383 5120f3cb Panagiotis Kanavos
                                    : JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
384 5120f3cb Panagiotis Kanavos
385 5120f3cb Panagiotis Kanavos
                    foreach (var info in infos)
386 5120f3cb Panagiotis Kanavos
                    {
387 5120f3cb Panagiotis Kanavos
                        info.Container = container;
388 5120f3cb Panagiotis Kanavos
                        info.Account = account;
389 5120f3cb Panagiotis Kanavos
                    }
390 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("START");
391 5120f3cb Panagiotis Kanavos
                    return infos;
392 cfed7823 Panagiotis Kanavos
                }
393 9c4346c9 Panagiotis Kanavos
            }
394 d78cbf09 Panagiotis Kanavos
        }
395 d78cbf09 Panagiotis Kanavos
396 637bc368 Panagiotis Kanavos
397 5bcf6d70 Panagiotis Kanavos
398 cfed7823 Panagiotis Kanavos
        public IList<ObjectInfo> ListObjects(string account, string container, string folder, DateTime? since = null)
399 637bc368 Panagiotis Kanavos
        {
400 637bc368 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
401 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("container");
402 9c4346c9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(folder))
403 9c4346c9 Panagiotis Kanavos
                throw new ArgumentNullException("folder");
404 9c4346c9 Panagiotis Kanavos
            Contract.EndContractBlock();
405 637bc368 Panagiotis Kanavos
406 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("List"))
407 9c4346c9 Panagiotis Kanavos
            {
408 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
409 cfed7823 Panagiotis Kanavos
410 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
411 5120f3cb Panagiotis Kanavos
                {
412 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
413 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
414 4d301e8e Panagiotis Kanavos
415 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
416 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("format", "json");
417 5120f3cb Panagiotis Kanavos
                    client.Parameters.Add("path", folder);
418 5120f3cb Panagiotis Kanavos
                    client.IfModifiedSince = since;
419 5120f3cb Panagiotis Kanavos
                    var content = client.DownloadStringWithRetry(container, 3);
420 5120f3cb Panagiotis Kanavos
                    client.AssertStatusOK("ListObjects failed");
421 5bcf6d70 Panagiotis Kanavos
422 5120f3cb Panagiotis Kanavos
                    var infos = JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
423 5120f3cb Panagiotis Kanavos
424 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
425 5120f3cb Panagiotis Kanavos
                    return infos;
426 5120f3cb Panagiotis Kanavos
                }
427 9c4346c9 Panagiotis Kanavos
            }
428 5bcf6d70 Panagiotis Kanavos
        }
429 5bcf6d70 Panagiotis Kanavos
430 d15e99b4 Panagiotis Kanavos
 
431 cfed7823 Panagiotis Kanavos
        public bool ContainerExists(string account, string container)
432 d78cbf09 Panagiotis Kanavos
        {
433 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
434 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
435 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
436 cfed7823 Panagiotis Kanavos
437 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Containters"].Push("Exists"))
438 d78cbf09 Panagiotis Kanavos
            {
439 5120f3cb Panagiotis Kanavos
                if (Log.IsDebugEnabled) Log.DebugFormat("START");
440 cfed7823 Panagiotis Kanavos
441 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
442 9c4346c9 Panagiotis Kanavos
                {
443 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
444 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
445 5120f3cb Panagiotis Kanavos
446 5120f3cb Panagiotis Kanavos
                    client.Parameters.Clear();
447 5120f3cb Panagiotis Kanavos
                    client.Head(container, 3);
448 5120f3cb Panagiotis Kanavos
                                        
449 5120f3cb Panagiotis Kanavos
                    bool result;
450 5120f3cb Panagiotis Kanavos
                    switch (client.StatusCode)
451 5120f3cb Panagiotis Kanavos
                    {
452 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.OK:
453 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.NoContent:
454 5120f3cb Panagiotis Kanavos
                            result=true;
455 5120f3cb Panagiotis Kanavos
                            break;
456 5120f3cb Panagiotis Kanavos
                        case HttpStatusCode.NotFound:
457 5120f3cb Panagiotis Kanavos
                            result=false;
458 5120f3cb Panagiotis Kanavos
                            break;
459 5120f3cb Panagiotis Kanavos
                        default:
460 5120f3cb Panagiotis Kanavos
                            throw CreateWebException("ContainerExists", client.StatusCode);
461 5120f3cb Panagiotis Kanavos
                    }
462 5120f3cb Panagiotis Kanavos
                    if (Log.IsDebugEnabled) Log.DebugFormat("END");
463 5120f3cb Panagiotis Kanavos
464 5120f3cb Panagiotis Kanavos
                    return result;
465 9c4346c9 Panagiotis Kanavos
                }
466 5120f3cb Panagiotis Kanavos
                
467 d78cbf09 Panagiotis Kanavos
            }
468 d78cbf09 Panagiotis Kanavos
        }
469 d78cbf09 Panagiotis Kanavos
470 cfed7823 Panagiotis Kanavos
        public bool ObjectExists(string account, string container, string objectName)
471 d78cbf09 Panagiotis Kanavos
        {
472 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
473 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
474 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
475 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
476 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
477 cfed7823 Panagiotis Kanavos
478 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
479 d78cbf09 Panagiotis Kanavos
            {
480 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
481 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
482 cfed7823 Panagiotis Kanavos
483 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
484 9c4346c9 Panagiotis Kanavos
                client.Head(container + "/" + objectName, 3);
485 9c4346c9 Panagiotis Kanavos
486 9c4346c9 Panagiotis Kanavos
                switch (client.StatusCode)
487 9c4346c9 Panagiotis Kanavos
                {
488 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.OK:
489 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NoContent:
490 9c4346c9 Panagiotis Kanavos
                        return true;
491 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NotFound:
492 9c4346c9 Panagiotis Kanavos
                        return false;
493 9c4346c9 Panagiotis Kanavos
                    default:
494 9c4346c9 Panagiotis Kanavos
                        throw CreateWebException("ObjectExists", client.StatusCode);
495 9c4346c9 Panagiotis Kanavos
                }
496 d78cbf09 Panagiotis Kanavos
            }
497 9c4346c9 Panagiotis Kanavos
498 d78cbf09 Panagiotis Kanavos
        }
499 d78cbf09 Panagiotis Kanavos
500 cfed7823 Panagiotis Kanavos
        public ObjectInfo GetObjectInfo(string account, string container, string objectName)
501 d78cbf09 Panagiotis Kanavos
        {
502 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
503 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
504 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
505 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
506 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
507 d78cbf09 Panagiotis Kanavos
508 5120f3cb Panagiotis Kanavos
            using (log4net.ThreadContext.Stacks["Objects"].Push("GetObjectInfo"))
509 5120f3cb Panagiotis Kanavos
            {                
510 5120f3cb Panagiotis Kanavos
511 5120f3cb Panagiotis Kanavos
                using (var client = new RestClient(_baseClient))
512 9c4346c9 Panagiotis Kanavos
                {
513 5120f3cb Panagiotis Kanavos
                    if (!String.IsNullOrWhiteSpace(account))
514 5120f3cb Panagiotis Kanavos
                        client.BaseAddress = GetAccountUrl(account);
515 5120f3cb Panagiotis Kanavos
                    try
516 5120f3cb Panagiotis Kanavos
                    {
517 5120f3cb Panagiotis Kanavos
                        client.Parameters.Clear();
518 d78cbf09 Panagiotis Kanavos
519 5120f3cb Panagiotis Kanavos
                        client.Head(container + "/" + objectName, 3);
520 d78cbf09 Panagiotis Kanavos
521 5120f3cb Panagiotis Kanavos
                        if (client.TimedOut)
522 5120f3cb Panagiotis Kanavos
                            return ObjectInfo.Empty;
523 9c4346c9 Panagiotis Kanavos
524 5120f3cb Panagiotis Kanavos
                        switch (client.StatusCode)
525 5120f3cb Panagiotis Kanavos
                        {
526 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.OK:
527 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.NoContent:
528 5120f3cb Panagiotis Kanavos
                                var keys = client.ResponseHeaders.AllKeys.AsQueryable();
529 5120f3cb Panagiotis Kanavos
                                var tags = (from key in keys
530 5120f3cb Panagiotis Kanavos
                                            where key.StartsWith("X-Object-Meta-")
531 5120f3cb Panagiotis Kanavos
                                            let name = key.Substring(14)
532 5120f3cb Panagiotis Kanavos
                                            select new {Name = name, Value = client.ResponseHeaders[name]})
533 5120f3cb Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
534 5120f3cb Panagiotis Kanavos
                                var extensions = (from key in keys
535 5120f3cb Panagiotis Kanavos
                                                  where key.StartsWith("X-Object-") && !key.StartsWith("X-Object-Meta-")
536 5120f3cb Panagiotis Kanavos
                                                  select new {Name = key, Value = client.ResponseHeaders[key]})
537 5120f3cb Panagiotis Kanavos
                                    .ToDictionary(t => t.Name, t => t.Value);
538 5120f3cb Panagiotis Kanavos
                                var info = new ObjectInfo
539 5120f3cb Panagiotis Kanavos
                                               {
540 7b0a5fec Panagiotis Kanavos
                                                   Account = account,
541 7b0a5fec Panagiotis Kanavos
                                                   Container = container,
542 5120f3cb Panagiotis Kanavos
                                                   Name = objectName,
543 5120f3cb Panagiotis Kanavos
                                                   Hash = client.GetHeaderValue("ETag"),
544 5120f3cb Panagiotis Kanavos
                                                   Content_Type = client.GetHeaderValue("Content-Type"),
545 7b0a5fec Panagiotis Kanavos
                                                   Bytes = Convert.ToInt64(client.GetHeaderValue("Content-Length")),
546 5120f3cb Panagiotis Kanavos
                                                   Tags = tags,
547 5120f3cb Panagiotis Kanavos
                                                   Last_Modified = client.LastModified,
548 5120f3cb Panagiotis Kanavos
                                                   Extensions = extensions
549 5120f3cb Panagiotis Kanavos
                                               };
550 5120f3cb Panagiotis Kanavos
                                return info;
551 5120f3cb Panagiotis Kanavos
                            case HttpStatusCode.NotFound:
552 5120f3cb Panagiotis Kanavos
                                return ObjectInfo.Empty;
553 5120f3cb Panagiotis Kanavos
                            default:
554 5120f3cb Panagiotis Kanavos
                                throw new WebException(
555 5120f3cb Panagiotis Kanavos
                                    String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}",
556 5120f3cb Panagiotis Kanavos
                                                  objectName, client.StatusCode));
557 5120f3cb Panagiotis Kanavos
                        }
558 5120f3cb Panagiotis Kanavos
559 5120f3cb Panagiotis Kanavos
                    }
560 5120f3cb Panagiotis Kanavos
                    catch (RetryException)
561 9c4346c9 Panagiotis Kanavos
                    {
562 5120f3cb Panagiotis Kanavos
                        Log.WarnFormat("[RETRY FAIL] GetObjectInfo for {0} failed.");
563 5120f3cb Panagiotis Kanavos
                        return ObjectInfo.Empty;
564 9c4346c9 Panagiotis Kanavos
                    }
565 5120f3cb Panagiotis Kanavos
                    catch (WebException e)
566 5120f3cb Panagiotis Kanavos
                    {
567 5120f3cb Panagiotis Kanavos
                        Log.Error(
568 5120f3cb Panagiotis Kanavos
                            String.Format("[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}",
569 5120f3cb Panagiotis Kanavos
                                          objectName, client.StatusCode), e);
570 5120f3cb Panagiotis Kanavos
                        throw;
571 5120f3cb Panagiotis Kanavos
                    }
572 5120f3cb Panagiotis Kanavos
                }                
573 d78cbf09 Panagiotis Kanavos
            }
574 9c4346c9 Panagiotis Kanavos
575 d78cbf09 Panagiotis Kanavos
        }
576 d78cbf09 Panagiotis Kanavos
577 cfed7823 Panagiotis Kanavos
        public void CreateFolder(string account, string container, string folder)
578 10523ad9 Panagiotis Kanavos
        {
579 10523ad9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
580 10523ad9 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
581 10523ad9 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(folder))
582 10523ad9 Panagiotis Kanavos
                throw new ArgumentNullException("folder", "The folder property can't be empty");
583 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
584 10523ad9 Panagiotis Kanavos
585 10523ad9 Panagiotis Kanavos
            var folderUrl=String.Format("{0}/{1}",container,folder);
586 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
587 9c4346c9 Panagiotis Kanavos
            {
588 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
589 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
590 cfed7823 Panagiotis Kanavos
591 9c4346c9 Panagiotis Kanavos
                client.Parameters.Clear();
592 9c4346c9 Panagiotis Kanavos
                client.Headers.Add("Content-Type", @"application/directory");
593 9c4346c9 Panagiotis Kanavos
                client.Headers.Add("Content-Length", "0");
594 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(folderUrl, 3);
595 10523ad9 Panagiotis Kanavos
596 9c4346c9 Panagiotis Kanavos
                if (client.StatusCode != HttpStatusCode.Created && client.StatusCode != HttpStatusCode.Accepted)
597 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("CreateFolder", client.StatusCode);
598 9c4346c9 Panagiotis Kanavos
            }
599 10523ad9 Panagiotis Kanavos
        }
600 10523ad9 Panagiotis Kanavos
601 cfed7823 Panagiotis Kanavos
        public ContainerInfo GetContainerInfo(string account, string container)
602 d78cbf09 Panagiotis Kanavos
        {
603 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
604 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
605 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
606 cfed7823 Panagiotis Kanavos
607 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
608 d78cbf09 Panagiotis Kanavos
            {
609 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
610 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);                
611 cfed7823 Panagiotis Kanavos
612 9c4346c9 Panagiotis Kanavos
                client.Head(container);
613 9c4346c9 Panagiotis Kanavos
                switch (client.StatusCode)
614 9c4346c9 Panagiotis Kanavos
                {
615 5ce54458 Panagiotis Kanavos
                    case HttpStatusCode.OK:
616 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NoContent:
617 9c4346c9 Panagiotis Kanavos
                        var containerInfo = new ContainerInfo
618 9c4346c9 Panagiotis Kanavos
                                                {
619 42800be8 Panagiotis Kanavos
                                                    Account=account,
620 9c4346c9 Panagiotis Kanavos
                                                    Name = container,
621 9c4346c9 Panagiotis Kanavos
                                                    Count =
622 9c4346c9 Panagiotis Kanavos
                                                        long.Parse(client.GetHeaderValue("X-Container-Object-Count")),
623 5ce54458 Panagiotis Kanavos
                                                    Bytes = long.Parse(client.GetHeaderValue("X-Container-Bytes-Used")),
624 5ce54458 Panagiotis Kanavos
                                                    BlockHash = client.GetHeaderValue("X-Container-Block-Hash"),
625 42800be8 Panagiotis Kanavos
                                                    BlockSize=int.Parse(client.GetHeaderValue("X-Container-Block-Size")),
626 42800be8 Panagiotis Kanavos
                                                    Last_Modified=client.LastModified
627 9c4346c9 Panagiotis Kanavos
                                                };
628 9c4346c9 Panagiotis Kanavos
                        return containerInfo;
629 9c4346c9 Panagiotis Kanavos
                    case HttpStatusCode.NotFound:
630 9c4346c9 Panagiotis Kanavos
                        return ContainerInfo.Empty;
631 9c4346c9 Panagiotis Kanavos
                    default:
632 9c4346c9 Panagiotis Kanavos
                        throw CreateWebException("GetContainerInfo", client.StatusCode);
633 9c4346c9 Panagiotis Kanavos
                }
634 d78cbf09 Panagiotis Kanavos
            }
635 d78cbf09 Panagiotis Kanavos
        }
636 d78cbf09 Panagiotis Kanavos
637 cfed7823 Panagiotis Kanavos
        public void CreateContainer(string account, string container)
638 cfed7823 Panagiotis Kanavos
        {            
639 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
640 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
641 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
642 cfed7823 Panagiotis Kanavos
643 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
644 9c4346c9 Panagiotis Kanavos
            {
645 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
646 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
647 cfed7823 Panagiotis Kanavos
648 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(container, 3);
649 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.Created, HttpStatusCode.Accepted, HttpStatusCode.OK};
650 9c4346c9 Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
651 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("CreateContainer", client.StatusCode);
652 9c4346c9 Panagiotis Kanavos
            }
653 d78cbf09 Panagiotis Kanavos
        }
654 d78cbf09 Panagiotis Kanavos
655 cfed7823 Panagiotis Kanavos
        public void DeleteContainer(string account, string container)
656 2c053915 Panagiotis Kanavos
        {
657 2c053915 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
658 2c053915 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
659 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
660 cfed7823 Panagiotis Kanavos
661 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
662 9c4346c9 Panagiotis Kanavos
            {
663 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
664 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
665 cfed7823 Panagiotis Kanavos
666 9c4346c9 Panagiotis Kanavos
                client.DeleteWithRetry(container, 3);
667 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent};
668 9c4346c9 Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
669 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("DeleteContainer", client.StatusCode);
670 9c4346c9 Panagiotis Kanavos
            }
671 2c053915 Panagiotis Kanavos
672 2c053915 Panagiotis Kanavos
        }
673 2c053915 Panagiotis Kanavos
674 5bcf6d70 Panagiotis Kanavos
        /// <summary>
675 5bcf6d70 Panagiotis Kanavos
        /// 
676 5bcf6d70 Panagiotis Kanavos
        /// </summary>
677 cfed7823 Panagiotis Kanavos
        /// <param name="account"></param>
678 5bcf6d70 Panagiotis Kanavos
        /// <param name="container"></param>
679 5bcf6d70 Panagiotis Kanavos
        /// <param name="objectName"></param>
680 283809f3 Panagiotis Kanavos
        /// <param name="fileName"></param>
681 5bcf6d70 Panagiotis Kanavos
        /// <returns></returns>
682 5ce54458 Panagiotis Kanavos
        /// <remarks>This method should have no timeout or a very long one</remarks>
683 5ce54458 Panagiotis Kanavos
        //Asynchronously download the object specified by *objectName* in a specific *container* to 
684 5ce54458 Panagiotis Kanavos
        // a local file
685 cfed7823 Panagiotis Kanavos
        public Task GetObject(string account, string container, string objectName, string fileName)
686 d78cbf09 Panagiotis Kanavos
        {
687 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
688 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
689 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
690 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");            
691 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
692 5ce54458 Panagiotis Kanavos
693 283809f3 Panagiotis Kanavos
            try
694 283809f3 Panagiotis Kanavos
            {
695 5ce54458 Panagiotis Kanavos
                //WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient
696 5ce54458 Panagiotis Kanavos
                //object to avoid concurrency errors.
697 5ce54458 Panagiotis Kanavos
                //
698 5ce54458 Panagiotis Kanavos
                //Download operations take a long time therefore they have no timeout.
699 9c4346c9 Panagiotis Kanavos
                var client = new RestClient(_baseClient) { Timeout = 0 };
700 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
701 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
702 cfed7823 Panagiotis Kanavos
703 cfed7823 Panagiotis Kanavos
                //The container and objectName are relative names. They are joined with the client's
704 cfed7823 Panagiotis Kanavos
                //BaseAddress to create the object's absolute address
705 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
706 cfed7823 Panagiotis Kanavos
                var uri = builder.Uri;
707 cfed7823 Panagiotis Kanavos
708 5ce54458 Panagiotis Kanavos
                //Download progress is reported to the Trace log
709 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[GET] START {0}", objectName);
710 283809f3 Panagiotis Kanavos
                client.DownloadProgressChanged += (sender, args) => 
711 5120f3cb Panagiotis Kanavos
                    Log.InfoFormat("[GET PROGRESS] {0} {1}% {2} of {3}",
712 283809f3 Panagiotis Kanavos
                                    fileName, args.ProgressPercentage,
713 283809f3 Panagiotis Kanavos
                                    args.BytesReceived,
714 5ce54458 Panagiotis Kanavos
                                    args.TotalBytesToReceive);                                
715 5ce54458 Panagiotis Kanavos
716 5ce54458 Panagiotis Kanavos
717 5ce54458 Panagiotis Kanavos
                //Start downloading the object asynchronously
718 5ce54458 Panagiotis Kanavos
                var downloadTask = client.DownloadFileTask(uri, fileName);
719 283809f3 Panagiotis Kanavos
                
720 5ce54458 Panagiotis Kanavos
                //Once the download completes
721 5ce54458 Panagiotis Kanavos
                return downloadTask.ContinueWith(download =>
722 283809f3 Panagiotis Kanavos
                                      {
723 5ce54458 Panagiotis Kanavos
                                          //Delete the local client object
724 283809f3 Panagiotis Kanavos
                                          client.Dispose();
725 5ce54458 Panagiotis Kanavos
                                          //And report failure or completion
726 283809f3 Panagiotis Kanavos
                                          if (download.IsFaulted)
727 283809f3 Panagiotis Kanavos
                                          {
728 5120f3cb Panagiotis Kanavos
                                              Log.ErrorFormat("[GET] FAIL for {0} with \r{1}", objectName,
729 283809f3 Panagiotis Kanavos
                                                               download.Exception);
730 283809f3 Panagiotis Kanavos
                                          }
731 283809f3 Panagiotis Kanavos
                                          else
732 283809f3 Panagiotis Kanavos
                                          {
733 5120f3cb Panagiotis Kanavos
                                              Log.InfoFormat("[GET] END {0}", objectName);                                             
734 283809f3 Panagiotis Kanavos
                                          }
735 283809f3 Panagiotis Kanavos
                                      });
736 283809f3 Panagiotis Kanavos
            }
737 283809f3 Panagiotis Kanavos
            catch (Exception exc)
738 d78cbf09 Panagiotis Kanavos
            {
739 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[GET] END {0} with {1}", objectName, exc);
740 283809f3 Panagiotis Kanavos
                throw;
741 d78cbf09 Panagiotis Kanavos
            }
742 283809f3 Panagiotis Kanavos
743 283809f3 Panagiotis Kanavos
744 283809f3 Panagiotis Kanavos
745 d78cbf09 Panagiotis Kanavos
        }
746 d78cbf09 Panagiotis Kanavos
747 cfed7823 Panagiotis Kanavos
        public Task<IList<string>> PutHashMap(string account, string container, string objectName, TreeHash hash)
748 5ce54458 Panagiotis Kanavos
        {
749 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
750 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("container");
751 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
752 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
753 5ce54458 Panagiotis Kanavos
            if (hash==null)
754 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("hash");
755 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
756 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
757 5ce54458 Panagiotis Kanavos
            if (StorageUrl == null)
758 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
759 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
760 cfed7823 Panagiotis Kanavos
761 cfed7823 Panagiotis Kanavos
762 cfed7823 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
763 cfed7823 Panagiotis Kanavos
            var client = new RestClient(_baseClient) { Timeout = 0 };
764 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
765 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
766 cfed7823 Panagiotis Kanavos
767 5ce54458 Panagiotis Kanavos
            //The container and objectName are relative names. They are joined with the client's
768 5ce54458 Panagiotis Kanavos
            //BaseAddress to create the object's absolute address
769 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, objectName);
770 5ce54458 Panagiotis Kanavos
            builder.Query = "format=json&hashmap";
771 5ce54458 Panagiotis Kanavos
            var uri = builder.Uri;
772 5ce54458 Panagiotis Kanavos
773 5ce54458 Panagiotis Kanavos
774 5ce54458 Panagiotis Kanavos
            //Send the tree hash as Json to the server            
775 5ce54458 Panagiotis Kanavos
            client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
776 5ce54458 Panagiotis Kanavos
            var uploadTask=client.UploadStringTask(uri, "PUT", hash.ToJson());
777 5ce54458 Panagiotis Kanavos
778 5ce54458 Panagiotis Kanavos
            
779 5ce54458 Panagiotis Kanavos
            return uploadTask.ContinueWith(t =>
780 5ce54458 Panagiotis Kanavos
            {
781 a27aa447 Panagiotis Kanavos
782 a27aa447 Panagiotis Kanavos
                var empty = (IList<string>)new List<string>();
783 5ce54458 Panagiotis Kanavos
                
784 5ce54458 Panagiotis Kanavos
785 5ce54458 Panagiotis Kanavos
                //The server will respond either with 201-created if all blocks were already on the server
786 a27aa447 Panagiotis Kanavos
                if (client.StatusCode == HttpStatusCode.Created)                    
787 a27aa447 Panagiotis Kanavos
                {
788 5ce54458 Panagiotis Kanavos
                    //in which case we return an empty hash list
789 a27aa447 Panagiotis Kanavos
                    return empty;
790 a27aa447 Panagiotis Kanavos
                }
791 5ce54458 Panagiotis Kanavos
                //or with a 409-conflict and return the list of missing parts
792 5ce54458 Panagiotis Kanavos
                //A 409 will cause an exception so we need to check t.IsFaulted to avoid propagating the exception                
793 5ce54458 Panagiotis Kanavos
                if (t.IsFaulted)
794 5ce54458 Panagiotis Kanavos
                {
795 5ce54458 Panagiotis Kanavos
                    var ex = t.Exception.InnerException;
796 5ce54458 Panagiotis Kanavos
                    var we = ex as WebException;
797 5ce54458 Panagiotis Kanavos
                    var response = we.Response as HttpWebResponse;
798 5ce54458 Panagiotis Kanavos
                    if (response!=null && response.StatusCode==HttpStatusCode.Conflict)
799 5ce54458 Panagiotis Kanavos
                    {
800 5ce54458 Panagiotis Kanavos
                        //In case of 409 the missing parts will be in the response content                        
801 5ce54458 Panagiotis Kanavos
                        using (var stream = response.GetResponseStream())
802 5ce54458 Panagiotis Kanavos
                        using(var reader=new StreamReader(stream))
803 5ce54458 Panagiotis Kanavos
                        {
804 a27aa447 Panagiotis Kanavos
                            //We need to cleanup the content before returning it because it contains
805 a27aa447 Panagiotis Kanavos
                            //error content after the list of hashes
806 a27aa447 Panagiotis Kanavos
                            var hashes = new List<string>();
807 a27aa447 Panagiotis Kanavos
                            string line=null;
808 a27aa447 Panagiotis Kanavos
                            //All lines up to the first empty line are hashes
809 a27aa447 Panagiotis Kanavos
                            while(!String.IsNullOrWhiteSpace(line=reader.ReadLine()))
810 a27aa447 Panagiotis Kanavos
                            {
811 a27aa447 Panagiotis Kanavos
                                hashes.Add(line);
812 a27aa447 Panagiotis Kanavos
                            }
813 a27aa447 Panagiotis Kanavos
814 a27aa447 Panagiotis Kanavos
                            return hashes;
815 5ce54458 Panagiotis Kanavos
                        }                        
816 5ce54458 Panagiotis Kanavos
                    }
817 5ce54458 Panagiotis Kanavos
                    else
818 5ce54458 Panagiotis Kanavos
                        //Any other status code is unexpected and the exception should be rethrown
819 5ce54458 Panagiotis Kanavos
                        throw ex;
820 5ce54458 Panagiotis Kanavos
                    
821 5ce54458 Panagiotis Kanavos
                }
822 5ce54458 Panagiotis Kanavos
                //Any other status code is unexpected but there was no exception. We can probably continue processing
823 5ce54458 Panagiotis Kanavos
                else
824 5ce54458 Panagiotis Kanavos
                {
825 5120f3cb Panagiotis Kanavos
                    Log.WarnFormat("Unexcpected status code when putting map: {0} - {1}",client.StatusCode,client.StatusDescription);                    
826 5ce54458 Panagiotis Kanavos
                }
827 a27aa447 Panagiotis Kanavos
                return empty;
828 5ce54458 Panagiotis Kanavos
            });
829 5ce54458 Panagiotis Kanavos
830 5ce54458 Panagiotis Kanavos
        }
831 5ce54458 Panagiotis Kanavos
832 cfed7823 Panagiotis Kanavos
        public Task<byte[]> GetBlock(string account, string container, Uri relativeUrl, long start, long? end)
833 a27aa447 Panagiotis Kanavos
        {
834 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
835 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
836 a27aa447 Panagiotis Kanavos
            if (StorageUrl == null)
837 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
838 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
839 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("container");
840 a27aa447 Panagiotis Kanavos
            if (relativeUrl== null)
841 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("relativeUrl");
842 a27aa447 Panagiotis Kanavos
            if (end.HasValue && end<0)
843 a27aa447 Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("end");
844 a27aa447 Panagiotis Kanavos
            if (start<0)
845 a27aa447 Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("start");
846 a27aa447 Panagiotis Kanavos
            Contract.EndContractBlock();
847 a27aa447 Panagiotis Kanavos
848 a27aa447 Panagiotis Kanavos
849 a27aa447 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
850 a27aa447 Panagiotis Kanavos
            var client = new RestClient(_baseClient) {Timeout = 0, RangeFrom = start, RangeTo = end};
851 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
852 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
853 cfed7823 Panagiotis Kanavos
854 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, relativeUrl.ToString());
855 cfed7823 Panagiotis Kanavos
            var uri = builder.Uri;
856 cfed7823 Panagiotis Kanavos
857 a27aa447 Panagiotis Kanavos
            return client.DownloadDataTask(uri)
858 a27aa447 Panagiotis Kanavos
                .ContinueWith(t=>
859 a27aa447 Panagiotis Kanavos
                                  {
860 a27aa447 Panagiotis Kanavos
                                      client.Dispose();
861 a27aa447 Panagiotis Kanavos
                                      return t.Result;
862 a27aa447 Panagiotis Kanavos
                                  });
863 a27aa447 Panagiotis Kanavos
        }
864 a27aa447 Panagiotis Kanavos
865 a27aa447 Panagiotis Kanavos
866 cfed7823 Panagiotis Kanavos
        public Task PostBlock(string account, string container, byte[] block, int offset, int count)
867 a27aa447 Panagiotis Kanavos
        {
868 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
869 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("container");
870 a27aa447 Panagiotis Kanavos
            if (block == null)
871 a27aa447 Panagiotis Kanavos
                throw new ArgumentNullException("block");
872 0af3141d Panagiotis Kanavos
            if (offset < 0 || offset >= block.Length)
873 0af3141d Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("offset");
874 0af3141d Panagiotis Kanavos
            if (count < 0 || count > block.Length)
875 0af3141d Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("count");
876 a27aa447 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
877 a27aa447 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
878 a27aa447 Panagiotis Kanavos
            if (StorageUrl == null)
879 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");                        
880 a27aa447 Panagiotis Kanavos
            Contract.EndContractBlock();
881 a27aa447 Panagiotis Kanavos
882 cfed7823 Panagiotis Kanavos
                        
883 cfed7823 Panagiotis Kanavos
            //Don't use a timeout because putting the hashmap may be a long process
884 cfed7823 Panagiotis Kanavos
            var client = new RestClient(_baseClient) { Timeout = 0 };
885 cfed7823 Panagiotis Kanavos
            if (!String.IsNullOrWhiteSpace(account))
886 cfed7823 Panagiotis Kanavos
                client.BaseAddress = GetAccountUrl(account);
887 cfed7823 Panagiotis Kanavos
888 cfed7823 Panagiotis Kanavos
            var builder = client.GetAddressBuilder(container, "");
889 a27aa447 Panagiotis Kanavos
            //We are doing an update
890 a27aa447 Panagiotis Kanavos
            builder.Query = "update";
891 a27aa447 Panagiotis Kanavos
            var uri = builder.Uri;
892 cfed7823 Panagiotis Kanavos
893 a27aa447 Panagiotis Kanavos
            client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
894 a27aa447 Panagiotis Kanavos
895 5120f3cb Panagiotis Kanavos
            Log.InfoFormat("[BLOCK POST] START");
896 a27aa447 Panagiotis Kanavos
897 a64c87c8 Panagiotis Kanavos
            client.UploadProgressChanged += (sender, args) => 
898 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST PROGRESS] {0}% {1} of {2}",
899 a64c87c8 Panagiotis Kanavos
                                    args.ProgressPercentage, args.BytesSent,
900 a64c87c8 Panagiotis Kanavos
                                    args.TotalBytesToSend);
901 a64c87c8 Panagiotis Kanavos
            client.UploadFileCompleted += (sender, args) => 
902 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST PROGRESS] Completed ");
903 a27aa447 Panagiotis Kanavos
904 a64c87c8 Panagiotis Kanavos
            
905 a64c87c8 Panagiotis Kanavos
            //Send the block
906 a64c87c8 Panagiotis Kanavos
            var uploadTask = client.UploadDataTask(uri, "POST", block)
907 a64c87c8 Panagiotis Kanavos
            .ContinueWith(upload =>
908 a27aa447 Panagiotis Kanavos
            {
909 a64c87c8 Panagiotis Kanavos
                client.Dispose();
910 a27aa447 Panagiotis Kanavos
911 a64c87c8 Panagiotis Kanavos
                if (upload.IsFaulted)
912 a27aa447 Panagiotis Kanavos
                {
913 a64c87c8 Panagiotis Kanavos
                    var exception = upload.Exception.InnerException;
914 5120f3cb Panagiotis Kanavos
                    Log.ErrorFormat("[BLOCK POST] FAIL with \r{0}", exception);                        
915 a64c87c8 Panagiotis Kanavos
                    throw exception;
916 a64c87c8 Panagiotis Kanavos
                }
917 a64c87c8 Panagiotis Kanavos
                    
918 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[BLOCK POST] END");
919 a64c87c8 Panagiotis Kanavos
            });
920 a27aa447 Panagiotis Kanavos
            return uploadTask;            
921 a27aa447 Panagiotis Kanavos
        }
922 a27aa447 Panagiotis Kanavos
923 5ce54458 Panagiotis Kanavos
924 cfed7823 Panagiotis Kanavos
        public Task<TreeHash> GetHashMap(string account, string container, string objectName)
925 5ce54458 Panagiotis Kanavos
        {
926 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
927 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("container");
928 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
929 5ce54458 Panagiotis Kanavos
                throw new ArgumentNullException("objectName");
930 5ce54458 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(Token))
931 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Token");
932 5ce54458 Panagiotis Kanavos
            if (StorageUrl == null)
933 5ce54458 Panagiotis Kanavos
                throw new InvalidOperationException("Invalid Storage Url");
934 5ce54458 Panagiotis Kanavos
            Contract.EndContractBlock();
935 5ce54458 Panagiotis Kanavos
936 5ce54458 Panagiotis Kanavos
            try
937 5ce54458 Panagiotis Kanavos
            {
938 5ce54458 Panagiotis Kanavos
                //WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient
939 5ce54458 Panagiotis Kanavos
                //object to avoid concurrency errors.
940 5ce54458 Panagiotis Kanavos
                //
941 5ce54458 Panagiotis Kanavos
                //Download operations take a long time therefore they have no timeout.
942 5ce54458 Panagiotis Kanavos
                //TODO: Do we really? this is a hashmap operation, not a download
943 5ce54458 Panagiotis Kanavos
                var client = new RestClient(_baseClient) { Timeout = 0 };
944 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
945 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
946 cfed7823 Panagiotis Kanavos
947 5ce54458 Panagiotis Kanavos
948 cfed7823 Panagiotis Kanavos
                //The container and objectName are relative names. They are joined with the client's
949 cfed7823 Panagiotis Kanavos
                //BaseAddress to create the object's absolute address
950 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
951 cfed7823 Panagiotis Kanavos
                builder.Query = "format=json&hashmap";
952 cfed7823 Panagiotis Kanavos
                var uri = builder.Uri;
953 cfed7823 Panagiotis Kanavos
                
954 5ce54458 Panagiotis Kanavos
                //Start downloading the object asynchronously
955 5ce54458 Panagiotis Kanavos
                var downloadTask = client.DownloadStringTask(uri);
956 5ce54458 Panagiotis Kanavos
                
957 5ce54458 Panagiotis Kanavos
                //Once the download completes
958 5ce54458 Panagiotis Kanavos
                return downloadTask.ContinueWith(download =>
959 5ce54458 Panagiotis Kanavos
                {
960 5ce54458 Panagiotis Kanavos
                    //Delete the local client object
961 5ce54458 Panagiotis Kanavos
                    client.Dispose();
962 5ce54458 Panagiotis Kanavos
                    //And report failure or completion
963 5ce54458 Panagiotis Kanavos
                    if (download.IsFaulted)
964 5ce54458 Panagiotis Kanavos
                    {
965 5120f3cb Panagiotis Kanavos
                        Log.ErrorFormat("[GET HASH] FAIL for {0} with \r{1}", objectName,
966 5ce54458 Panagiotis Kanavos
                                        download.Exception);
967 5ce54458 Panagiotis Kanavos
                        throw download.Exception;
968 5ce54458 Panagiotis Kanavos
                    }
969 5ce54458 Panagiotis Kanavos
                                          
970 5ce54458 Panagiotis Kanavos
                    //The server will return an empty string if the file is empty
971 5ce54458 Panagiotis Kanavos
                    var json = download.Result;
972 5ce54458 Panagiotis Kanavos
                    var treeHash = TreeHash.Parse(json);
973 5120f3cb Panagiotis Kanavos
                    Log.InfoFormat("[GET HASH] END {0}", objectName);                                             
974 5ce54458 Panagiotis Kanavos
                    return treeHash;
975 5ce54458 Panagiotis Kanavos
                });
976 5ce54458 Panagiotis Kanavos
            }
977 5ce54458 Panagiotis Kanavos
            catch (Exception exc)
978 5ce54458 Panagiotis Kanavos
            {
979 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[GET HASH] END {0} with {1}", objectName, exc);
980 5ce54458 Panagiotis Kanavos
                throw;
981 5ce54458 Panagiotis Kanavos
            }
982 5ce54458 Panagiotis Kanavos
983 5ce54458 Panagiotis Kanavos
984 5ce54458 Panagiotis Kanavos
985 5ce54458 Panagiotis Kanavos
        }
986 5ce54458 Panagiotis Kanavos
987 5ce54458 Panagiotis Kanavos
988 5bcf6d70 Panagiotis Kanavos
        /// <summary>
989 5bcf6d70 Panagiotis Kanavos
        /// 
990 5bcf6d70 Panagiotis Kanavos
        /// </summary>
991 cfed7823 Panagiotis Kanavos
        /// <param name="account"></param>
992 5bcf6d70 Panagiotis Kanavos
        /// <param name="container"></param>
993 5bcf6d70 Panagiotis Kanavos
        /// <param name="objectName"></param>
994 5bcf6d70 Panagiotis Kanavos
        /// <param name="fileName"></param>
995 b5061ac8 Panagiotis Kanavos
        /// <param name="hash">Optional hash value for the file. If no hash is provided, the method calculates a new hash</param>
996 5bcf6d70 Panagiotis Kanavos
        /// <remarks>>This method should have no timeout or a very long one</remarks>
997 cfed7823 Panagiotis Kanavos
        public Task PutObject(string account, string container, string objectName, string fileName, string hash = null)
998 d78cbf09 Panagiotis Kanavos
        {
999 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(container))
1000 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("container", "The container property can't be empty");
1001 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
1002 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The objectName property can't be empty");
1003 b6c72f62 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(fileName))
1004 b6c72f62 Panagiotis Kanavos
                throw new ArgumentNullException("fileName", "The fileName property can't be empty");
1005 637bc368 Panagiotis Kanavos
            if (!File.Exists(fileName))
1006 637bc368 Panagiotis Kanavos
                throw new FileNotFoundException("The file does not exist",fileName);
1007 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1008 b6c72f62 Panagiotis Kanavos
            
1009 b5061ac8 Panagiotis Kanavos
            try
1010 b5061ac8 Panagiotis Kanavos
            {
1011 cfed7823 Panagiotis Kanavos
1012 cfed7823 Panagiotis Kanavos
                var client = new RestClient(_baseClient){Timeout=0};
1013 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1014 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1015 cfed7823 Panagiotis Kanavos
1016 cfed7823 Panagiotis Kanavos
                var builder = client.GetAddressBuilder(container, objectName);
1017 5ce54458 Panagiotis Kanavos
                var uri = builder.Uri;
1018 0eea575a Panagiotis Kanavos
1019 0eea575a Panagiotis Kanavos
                string etag = hash ?? CalculateHash(fileName);
1020 0eea575a Panagiotis Kanavos
1021 0eea575a Panagiotis Kanavos
                client.Headers.Add("Content-Type", "application/octet-stream");
1022 0eea575a Panagiotis Kanavos
                client.Headers.Add("ETag", etag);
1023 0eea575a Panagiotis Kanavos
1024 0eea575a Panagiotis Kanavos
1025 5120f3cb Panagiotis Kanavos
                Log.InfoFormat("[PUT] START {0}", objectName);
1026 0eea575a Panagiotis Kanavos
                client.UploadProgressChanged += (sender, args) =>
1027 0eea575a Panagiotis Kanavos
                {
1028 5120f3cb Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["PUT"].Push("Progress"))
1029 5120f3cb Panagiotis Kanavos
                    {
1030 5120f3cb Panagiotis Kanavos
                        Log.InfoFormat("{0} {1}% {2} of {3}", fileName, args.ProgressPercentage,
1031 5120f3cb Panagiotis Kanavos
                                       args.BytesSent, args.TotalBytesToSend);
1032 5120f3cb Panagiotis Kanavos
                    }
1033 0eea575a Panagiotis Kanavos
                };
1034 bfc13ed8 Panagiotis Kanavos
1035 bfc13ed8 Panagiotis Kanavos
                client.UploadFileCompleted += (sender, args) =>
1036 bfc13ed8 Panagiotis Kanavos
                {
1037 5120f3cb Panagiotis Kanavos
                    using (log4net.ThreadContext.Stacks["PUT"].Push("Progress"))
1038 5120f3cb Panagiotis Kanavos
                    {
1039 5120f3cb Panagiotis Kanavos
                        Log.InfoFormat("Completed {0}", fileName);
1040 5120f3cb Panagiotis Kanavos
                    }
1041 bfc13ed8 Panagiotis Kanavos
                };
1042 0eea575a Panagiotis Kanavos
                return client.UploadFileTask(uri, "PUT", fileName)
1043 0eea575a Panagiotis Kanavos
                    .ContinueWith(upload=>
1044 0eea575a Panagiotis Kanavos
                                      {
1045 0eea575a Panagiotis Kanavos
                                          client.Dispose();
1046 0eea575a Panagiotis Kanavos
1047 0eea575a Panagiotis Kanavos
                                          if (upload.IsFaulted)
1048 a27aa447 Panagiotis Kanavos
                                          {
1049 a27aa447 Panagiotis Kanavos
                                              var exc = upload.Exception.InnerException;
1050 5120f3cb Panagiotis Kanavos
                                              Log.ErrorFormat("[PUT] FAIL for {0} with \r{1}",objectName,exc);
1051 a27aa447 Panagiotis Kanavos
                                              throw exc;
1052 0eea575a Panagiotis Kanavos
                                          }
1053 0eea575a Panagiotis Kanavos
                                          else
1054 5120f3cb Panagiotis Kanavos
                                            Log.InfoFormat("[PUT] END {0}", objectName);
1055 0eea575a Panagiotis Kanavos
                                      });
1056 b5061ac8 Panagiotis Kanavos
            }
1057 b5061ac8 Panagiotis Kanavos
            catch (Exception exc)
1058 b5061ac8 Panagiotis Kanavos
            {
1059 5120f3cb Panagiotis Kanavos
                Log.ErrorFormat("[PUT] END {0} with {1}", objectName, exc);
1060 b5061ac8 Panagiotis Kanavos
                throw;
1061 b5061ac8 Panagiotis Kanavos
            }                
1062 b5061ac8 Panagiotis Kanavos
1063 0eea575a Panagiotis Kanavos
        }
1064 0050438e Panagiotis Kanavos
       
1065 4d301e8e Panagiotis Kanavos
        
1066 b6c72f62 Panagiotis Kanavos
        private static string CalculateHash(string fileName)
1067 d78cbf09 Panagiotis Kanavos
        {
1068 0bd56b7c Panagiotis Kanavos
            Contract.Requires(!String.IsNullOrWhiteSpace(fileName));
1069 0bd56b7c Panagiotis Kanavos
            Contract.EndContractBlock();
1070 0bd56b7c Panagiotis Kanavos
1071 d78cbf09 Panagiotis Kanavos
            string hash;
1072 d78cbf09 Panagiotis Kanavos
            using (var hasher = MD5.Create())
1073 b6c72f62 Panagiotis Kanavos
            using(var stream=File.OpenRead(fileName))
1074 d78cbf09 Panagiotis Kanavos
            {
1075 d78cbf09 Panagiotis Kanavos
                var hashBuilder=new StringBuilder();
1076 b6c72f62 Panagiotis Kanavos
                foreach (byte b in hasher.ComputeHash(stream))
1077 d78cbf09 Panagiotis Kanavos
                    hashBuilder.Append(b.ToString("x2").ToLower());
1078 d78cbf09 Panagiotis Kanavos
                hash = hashBuilder.ToString();                
1079 d78cbf09 Panagiotis Kanavos
            }
1080 d78cbf09 Panagiotis Kanavos
            return hash;
1081 d78cbf09 Panagiotis Kanavos
        }
1082 0bd56b7c Panagiotis Kanavos
        
1083 cfed7823 Panagiotis Kanavos
        public void MoveObject(string account, string sourceContainer, string oldObjectName, string targetContainer, string newObjectName)
1084 d78cbf09 Panagiotis Kanavos
        {
1085 3c43ec9b Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(sourceContainer))
1086 3c43ec9b Panagiotis Kanavos
                throw new ArgumentNullException("sourceContainer", "The container property can't be empty");
1087 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(oldObjectName))
1088 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("oldObjectName", "The oldObjectName property can't be empty");
1089 3c43ec9b Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(targetContainer))
1090 3c43ec9b Panagiotis Kanavos
                throw new ArgumentNullException("targetContainer", "The container property can't be empty");
1091 d78cbf09 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(newObjectName))
1092 d78cbf09 Panagiotis Kanavos
                throw new ArgumentNullException("newObjectName", "The newObjectName property can't be empty");
1093 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1094 d78cbf09 Panagiotis Kanavos
1095 3c43ec9b Panagiotis Kanavos
            var targetUrl = targetContainer + "/" + newObjectName;
1096 3c43ec9b Panagiotis Kanavos
            var sourceUrl = String.Format("/{0}/{1}", sourceContainer, oldObjectName);
1097 3c43ec9b Panagiotis Kanavos
1098 9c4346c9 Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
1099 d78cbf09 Panagiotis Kanavos
            {
1100 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1101 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1102 cfed7823 Panagiotis Kanavos
1103 0af3141d Panagiotis Kanavos
                client.Headers.Add("X-Move-From", sourceUrl);
1104 9c4346c9 Panagiotis Kanavos
                client.PutWithRetry(targetUrl, 3);
1105 9c4346c9 Panagiotis Kanavos
1106 9c4346c9 Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created};
1107 0af3141d Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
1108 9c4346c9 Panagiotis Kanavos
                    throw CreateWebException("MoveObject", client.StatusCode);
1109 9c4346c9 Panagiotis Kanavos
            }
1110 d78cbf09 Panagiotis Kanavos
        }
1111 d78cbf09 Panagiotis Kanavos
1112 c53aa229 Panagiotis Kanavos
        public void DeleteObject(string account, string sourceContainer, string objectName)
1113 0af3141d Panagiotis Kanavos
        {            
1114 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(sourceContainer))
1115 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("sourceContainer", "The container property can't be empty");
1116 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(objectName))
1117 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("objectName", "The oldObjectName property can't be empty");
1118 cfed7823 Panagiotis Kanavos
            Contract.EndContractBlock();
1119 0af3141d Panagiotis Kanavos
1120 c53aa229 Panagiotis Kanavos
            var targetUrl = FolderConstants.TrashContainer + "/" + objectName;
1121 0af3141d Panagiotis Kanavos
            var sourceUrl = String.Format("/{0}/{1}", sourceContainer, objectName);
1122 0af3141d Panagiotis Kanavos
1123 0af3141d Panagiotis Kanavos
            using (var client = new RestClient(_baseClient))
1124 0af3141d Panagiotis Kanavos
            {
1125 cfed7823 Panagiotis Kanavos
                if (!String.IsNullOrWhiteSpace(account))
1126 cfed7823 Panagiotis Kanavos
                    client.BaseAddress = GetAccountUrl(account);
1127 cfed7823 Panagiotis Kanavos
1128 0af3141d Panagiotis Kanavos
                client.Headers.Add("X-Move-From", sourceUrl);
1129 c53aa229 Panagiotis Kanavos
                client.AllowedStatusCodes.Add(HttpStatusCode.NotFound);
1130 0af3141d Panagiotis Kanavos
                client.PutWithRetry(targetUrl, 3);
1131 0af3141d Panagiotis Kanavos
1132 0af3141d Panagiotis Kanavos
                var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created,HttpStatusCode.NotFound};
1133 0af3141d Panagiotis Kanavos
                if (!expectedCodes.Contains(client.StatusCode))
1134 0af3141d Panagiotis Kanavos
                    throw CreateWebException("DeleteObject", client.StatusCode);
1135 0af3141d Panagiotis Kanavos
            }
1136 0af3141d Panagiotis Kanavos
        }
1137 0af3141d Panagiotis Kanavos
1138 4d301e8e Panagiotis Kanavos
      
1139 3c43ec9b Panagiotis Kanavos
        private static WebException CreateWebException(string operation, HttpStatusCode statusCode)
1140 3c43ec9b Panagiotis Kanavos
        {
1141 3c43ec9b Panagiotis Kanavos
            return new WebException(String.Format("{0} failed with unexpected status code {1}", operation, statusCode));
1142 3c43ec9b Panagiotis Kanavos
        }
1143 3c43ec9b Panagiotis Kanavos
1144 d15e99b4 Panagiotis Kanavos
        
1145 d78cbf09 Panagiotis Kanavos
    }
1146 cfed7823 Panagiotis Kanavos
1147 cfed7823 Panagiotis Kanavos
    public class ShareAccountInfo
1148 cfed7823 Panagiotis Kanavos
    {
1149 cfed7823 Panagiotis Kanavos
        public DateTime? last_modified { get; set; }
1150 cfed7823 Panagiotis Kanavos
        public string name { get; set; }
1151 cfed7823 Panagiotis Kanavos
    }
1152 d78cbf09 Panagiotis Kanavos
}