[Export(typeof(ICloudClient))]\r
public class CloudFilesClient:ICloudClient\r
{\r
+ private const string TOKEN_HEADER = "X-Auth-Token";\r
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
\r
//CloudFilesClient uses *_baseClient* internally to communicate with the server\r
private RestClient _baseClient;\r
\r
private HttpClient _baseHttpClient;\r
+ private HttpClient _baseHttpClientNoTimeout;\r
\r
\r
//During authentication the client provides a UserName \r
set\r
{\r
_token = value;\r
- _baseClient.Headers["X-Auth-Token"] = value;\r
+ _baseClient.Headers[TOKEN_HEADER] = value;\r
}\r
}\r
\r
BaseAddress = StorageUrl,\r
Timeout = TimeSpan.FromSeconds(30)\r
};\r
- _baseHttpClient.DefaultRequestHeaders.Add("X-Auth-Token", Token);\r
+ _baseHttpClient.DefaultRequestHeaders.Add(TOKEN_HEADER, Token);\r
+\r
+ _baseHttpClientNoTimeout = new HttpClient(httpClientHandler)\r
+ {\r
+ BaseAddress = StorageUrl,\r
+ Timeout = TimeSpan.FromMilliseconds(-1)\r
+ };\r
+ _baseHttpClientNoTimeout.DefaultRequestHeaders.Add(TOKEN_HEADER, Token);\r
\r
\r
}\r
if (String.IsNullOrWhiteSpace(storageUrl))\r
throw new InvalidOperationException("Failed to obtain storage url");\r
\r
- token = response.Headers.GetValues("X-Auth-Token").First();\r
+ token = response.Headers.GetValues(TOKEN_HEADER).First();\r
if (String.IsNullOrWhiteSpace(token))\r
throw new InvalidOperationException("Failed to obtain token url");\r
\r
BaseAddress = StorageUrl,\r
Timeout = TimeSpan.FromSeconds(30)\r
};\r
- _baseHttpClient.DefaultRequestHeaders.Add("X-Auth-Token", token);\r
+ _baseHttpClient.DefaultRequestHeaders.Add(TOKEN_HEADER, token);\r
+\r
+ _baseHttpClientNoTimeout = new HttpClient(httpClientHandler)\r
+ {\r
+ BaseAddress = StorageUrl,\r
+ Timeout = TimeSpan.FromMilliseconds(-1)\r
+ };\r
+ _baseHttpClientNoTimeout.DefaultRequestHeaders.Add(TOKEN_HEADER, token);\r
\r
/* var keys = authClient.ResponseHeaders.AllKeys.AsQueryable();\r
groups = (from key in keys\r
\r
}\r
\r
+ public async Task<IList<string>> PutHashMap(string account, Uri container, Uri objectName, TreeHash hash)\r
+ {\r
+ if (container == null)\r
+ throw new ArgumentNullException("container", "The container property can't be empty");\r
+ if (container.IsAbsoluteUri)\r
+ throw new ArgumentException("The container must be relative","container");\r
+ if (objectName == null)\r
+ throw new ArgumentNullException("objectName", "The objectName property can't be empty");\r
+ if (objectName.IsAbsoluteUri)\r
+ throw new ArgumentException("The objectName must be relative","objectName");\r
+ if (hash == null)\r
+ throw new ArgumentNullException("hash");\r
+ if (String.IsNullOrWhiteSpace(Token))\r
+ throw new InvalidOperationException("Invalid Token");\r
+ if (StorageUrl == null)\r
+ throw new InvalidOperationException("Invalid Storage Url");\r
+ Contract.EndContractBlock();\r
+\r
+ \r
+\r
+ //The container and objectName are relative names. They are joined with the client's\r
+ //BaseAddress to create the object's absolute address\r
+\r
+ var targetUri = GetTargetUri(account).Combine(container).Combine(objectName);\r
+ \r
+\r
+ var uri = new Uri(String.Format("{0}?format=json&hashmap",targetUri),UriKind.Absolute);\r
+\r
+ \r
+ //Send the tree hash as Json to the server \r
+ var jsonHash = hash.ToJson();\r
+ if (Log.IsDebugEnabled)\r
+ Log.DebugFormat("Hashes:\r\n{0}", jsonHash);\r
+\r
+ var message = new HttpRequestMessage(HttpMethod.Put, uri)\r
+ {\r
+ Content = new StringContent(jsonHash)\r
+ };\r
+ message.Headers.Add("ETag",hash.TopHash.ToHashString());\r
+ \r
+ //Don't use a timeout because putting the hashmap may be a long process\r
+\r
+ using(var response=await _baseHttpClientNoTimeout.SendAsyncWithRetries(message,3))\r
+ {\r
+ var empty = (IList<string>)new List<string>();\r
+ \r
+ switch (response.StatusCode)\r
+ {\r
+ case HttpStatusCode.Created:\r
+ //The server will respond either with 201-created if all blocks were already on the server\r
+ return empty;\r
+ case HttpStatusCode.Conflict:\r
+ //or with a 409-conflict and return the list of missing parts\r
+ using (var stream = await response.Content.ReadAsStreamAsync())\r
+ using(var reader=stream.GetLoggedReader(Log))\r
+ { \r
+ var serializer = new JsonSerializer(); \r
+ serializer.Error += (sender, args) => Log.ErrorFormat("Deserialization error at [{0}] [{1}]", args.ErrorContext.Error, args.ErrorContext.Member);\r
+ var hashes = (List<string>)serializer.Deserialize(reader, typeof(List<string>));\r
+ return hashes;\r
+ } \r
+ default:\r
+ //All other cases are unexpected\r
+ //Ensure that failure codes raise exceptions\r
+ response.EnsureSuccessStatusCode();\r
+ //And log any other codes as warngings, but continute processing\r
+ Log.WarnFormat("Unexcpected status code when putting map: {0} - {1}",response.StatusCode,response.ReasonPhrase);\r
+ return empty;\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+/*\r
public Task<IList<string>> PutHashMap(string account, Uri container, Uri objectName, TreeHash hash)\r
{\r
if (container == null)\r
\r
}\r
\r
+*/\r
\r
public async Task<byte[]> GetBlock(string account, Uri container, Uri relativeUrl, long start, long? end, CancellationToken cancellationToken)\r
{\r