Now closing web request immediatelly after executing a request that has no content.
authorPanagiotis Kanavos <pkanavos@gmail.com>
Tue, 3 Jan 2012 15:35:00 +0000 (17:35 +0200)
committerPanagiotis Kanavos <pkanavos@gmail.com>
Tue, 3 Jan 2012 15:35:00 +0000 (17:35 +0200)
Fixed content logging that improperly disposed the response stream in RestClient.cs

trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
trunk/Pithos.Core/Agents/NetworkAgent.cs
trunk/Pithos.Network/CloudFilesClient.cs
trunk/Pithos.Network/RestClient.cs

index 92cd900..e8b9fa7 100644 (file)
@@ -131,12 +131,18 @@ namespace Pithos.Client.WPF {
         private async Task StartMonitoring()
         {
             try
-            {                  
+            {
+                var accounts = Settings.Accounts.Select(MonitorAccount);
+                await TaskEx.WhenAll(accounts);
+                _statusService = StatusService.Start();
+
+/*
                 foreach (var account in Settings.Accounts)
                 {
                     await MonitorAccount(account);
                 }
-                _statusService = StatusService.Start();
+*/
+                
             }
             catch (AggregateException exc)
             {
index e609dd6..40f84d6 100644 (file)
@@ -478,8 +478,10 @@ namespace Pithos.Core.Agents
             var fileAgent = GetFileAgent(accountInfo);
             foreach (var trashObject in trashObjects)
             {
-                var relativePath = trashObject.RelativeUrlToFilePath(accountInfo.UserName);
-                //and remove any matching objects from the list, adding them to the commonObjects list
+                var barePath = trashObject.RelativeUrlToFilePath(accountInfo.UserName);
+                //HACK: Assume only the "pithos" container is used. Must find out what happens when
+                //deleting a file from a different container
+                var relativePath = Path.Combine("pithos", barePath);
                 fileAgent.Delete(relativePath);                                
             }
         }
@@ -747,12 +749,12 @@ namespace Pithos.Core.Agents
             try
             {
                 if (action == null)
-                    throw new ArgumentNullException("action");            
+                    throw new ArgumentNullException("action");
                 Contract.EndContractBlock();
 
-                var accountInfo=action.AccountInfo;
-            
-                var fileInfo=action.LocalFile;                        
+                var accountInfo = action.AccountInfo;
+
+                var fileInfo = action.LocalFile;
 
                 if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
                     return;
@@ -760,27 +762,27 @@ namespace Pithos.Core.Agents
                 var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
                 if (relativePath.StartsWith(FolderConstants.OthersFolder))
                 {
-                    var parts=relativePath.Split('\\');
+                    var parts = relativePath.Split('\\');
                     var accountName = parts[1];
                     var oldName = accountInfo.UserName;
                     var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
-                    var nameIndex=absoluteUri.IndexOf(oldName);
-                    var root=absoluteUri.Substring(0, nameIndex);
+                    var nameIndex = absoluteUri.IndexOf(oldName);
+                    var root = absoluteUri.Substring(0, nameIndex);
 
                     accountInfo = new AccountInfo
                     {
                         UserName = accountName,
                         AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
                         StorageUri = new Uri(root + accountName),
-                        BlockHash=accountInfo.BlockHash,
-                        BlockSize=accountInfo.BlockSize,
-                        Token=accountInfo.Token
+                        BlockHash = accountInfo.BlockHash,
+                        BlockSize = accountInfo.BlockSize,
+                        Token = accountInfo.Token
                     };
                 }
 
 
                 var fullFileName = fileInfo.FullName;
-                using(var gate=NetworkGate.Acquire(fullFileName,NetworkOperation.Uploading))
+                using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
                 {
                     //Abort if the file is already being uploaded or downloaded
                     if (gate.Failed)
@@ -791,14 +793,14 @@ namespace Pithos.Core.Agents
 
                     var client = new CloudFilesClient(accountInfo);
                     //Even if GetObjectInfo times out, we can proceed with the upload            
-                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);                
+                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
                     var cloudHash = info.Hash.ToLower();
 
                     var hash = action.LocalHash.Value;
                     var topHash = action.TopHash.Value;
 
                     //If the file hashes match, abort the upload
-                    if (hash == cloudHash  || topHash ==cloudHash)
+                    if (hash == cloudHash || topHash == cloudHash)
                     {
                         //but store any metadata changes 
                         this.StatusKeeper.StoreInfo(fullFileName, info);
@@ -806,7 +808,7 @@ namespace Pithos.Core.Agents
                         return;
                     }
 
-                    if (info.AllowedTo=="read")
+                    if (info.AllowedTo == "read")
                         return;
 
                     //Mark the file as modified while we upload it
@@ -818,9 +820,9 @@ namespace Pithos.Core.Agents
 
                     //First, calculate the tree hash
                     var treeHash = await Signature.CalculateTreeHashAsync(fileInfo.FullName, accountInfo.BlockSize,
-                        accountInfo.BlockHash);                
-                    
-                    await UploadWithHashMap(accountInfo,cloudFile,fileInfo,cloudFile.Name,treeHash);
+                        accountInfo.BlockHash);
+
+                    await UploadWithHashMap(accountInfo, cloudFile, fileInfo, cloudFile.Name, treeHash);
 
                     //If everything succeeds, change the file and overlay status to normal
                     this.StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
@@ -834,22 +836,40 @@ namespace Pithos.Core.Agents
                 var exc = ex.InnerException as WebException;
                 if (exc == null)
                     throw ex.InnerException;
-                var response = exc.Response as HttpWebResponse;
-                if (response == null)
-                    throw exc;
-                if (response.StatusCode == HttpStatusCode.Unauthorized)
-                {
-                    Log.Error("Not allowed to upload file", exc);
-                    var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
-                    StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
-                    StatusNotification.NotifyChange(message, TraceLevel.Warning);
+                if (HandleUploadWebException(action, exc)) 
                     return;
-                }
+                throw;
+            }
+            catch (WebException ex)
+            {
+                if (HandleUploadWebException(action, ex))
+                    return;
+                throw;
+            }
+            catch (Exception ex)
+            {
+                Log.Error("Unexpected error while uploading file", ex);
                 throw;
             }
 
         }
 
+        private bool HandleUploadWebException(CloudAction action, WebException exc)
+        {
+            var response = exc.Response as HttpWebResponse;
+            if (response == null)
+                throw exc;
+            if (response.StatusCode == HttpStatusCode.Unauthorized)
+            {
+                Log.Error("Not allowed to upload file", exc);
+                var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
+                StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
+                StatusNotification.NotifyChange(message, TraceLevel.Warning);
+                return true;
+            }
+            return false;
+        }
+
         public async Task UploadWithHashMap(AccountInfo accountInfo,ObjectInfo cloudFile,FileInfo fileInfo,string url,TreeHash treeHash)
         {
             if (accountInfo == null)
index cc3502c..2a112de 100644 (file)
@@ -7,6 +7,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.ComponentModel.Composition;
+using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.IO;
 using System.Linq;
@@ -946,7 +947,7 @@ namespace Pithos.Network
 
 
             //Don't use a timeout because putting the hashmap may be a long process
-            var client = new RestClient(_baseClient) { Timeout = 0 };
+            var client = new RestClient(_baseClient) { Timeout = 0 };           
             if (!String.IsNullOrWhiteSpace(account))
                 client.BaseAddress = GetAccountUrl(account);
 
@@ -959,8 +960,8 @@ namespace Pithos.Network
 
             //Send the tree hash as Json to the server            
             client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
-            var uploadTask=client.UploadStringTask(uri, "PUT", hash.ToJson());
-
+            var jsonHash = hash.ToJson();
+            var uploadTask=client.UploadStringTask(uri, "PUT", jsonHash);
             
             return uploadTask.ContinueWith(t =>
             {
@@ -987,6 +988,7 @@ namespace Pithos.Network
                         using (var stream = response.GetResponseStream())
                         using(var reader=new StreamReader(stream))
                         {
+                            Debug.Assert(stream.Position == 0);
                             //We need to cleanup the content before returning it because it contains
                             //error content after the list of hashes
                             var hashes = new List<string>();
index a2c5e90..82db2d1 100644 (file)
@@ -85,26 +85,6 @@ namespace Pithos.Network
         }
 
 
-        private WebHeaderCollection _responseHeaders;
-
-        public new WebHeaderCollection ResponseHeaders
-        {
-            get
-            {
-                if (base.ResponseHeaders==null)
-                {
-                    return _responseHeaders;
-                }
-                else
-                {
-                    _responseHeaders = null;
-                    return base.ResponseHeaders;   
-                }
-                
-            }
-
-            set { _responseHeaders = value; }
-        } 
         protected override WebRequest GetWebRequest(Uri address)
         {
             TimedOut = false;
@@ -187,7 +167,7 @@ namespace Pithos.Network
             //Does the response have any content to log?
             if (exc.Response.ContentLength > 0)
             {
-                var content = GetContent(exc.Response);
+                var content = LogContent(exc.Response);
                 Log.ErrorFormat(content);
             }
             return false;
@@ -205,19 +185,24 @@ namespace Pithos.Network
 
         public DateTime LastModified { get; private set; }
 
-        private static string GetContent(WebResponse webResponse)
+        private static string LogContent(WebResponse webResponse)
         {
             if (webResponse == null)
                 throw new ArgumentNullException("webResponse");
             Contract.EndContractBlock();
 
-            string content;
-            using (var stream = webResponse.GetResponseStream())
-            using (var reader = new StreamReader(stream))
+            //The response stream must be copied to avoid affecting other code by disposing of the 
+            //original response stream.
+            var stream = webResponse.GetResponseStream();            
+            using(var memStream=new MemoryStream((int) stream.Length))
+            using (var reader = new StreamReader(memStream))
             {
-                content = reader.ReadToEnd();
+                stream.CopyTo(memStream);                
+                string content = reader.ReadToEnd();
+
+                stream.Seek(0,SeekOrigin.Begin);
+                return content;
             }
-            return content;
         }
 
         public string DownloadStringWithRetry(string address,int retries=0)
@@ -232,10 +217,10 @@ namespace Pithos.Network
             
             var actualRetries = (retries == 0) ? Retries : retries;
 
-            
+            var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress);
+
             var task = Retry(() =>
-            {
-                var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress);                
+            {                
                 var content = base.DownloadString(uriString);
 
                 if (StatusCode == HttpStatusCode.NoContent)
@@ -308,15 +293,20 @@ namespace Pithos.Network
                 if (method == "PUT")
                     request.ContentLength = 0;
 
+                //Have to use try/finally instead of using here, because WebClient needs a valid WebResponse object
+                //in order to return response headers
                 var response = (HttpWebResponse)GetWebResponse(request);
-                //var response = (HttpWebResponse)request.GetResponse();
+                try
+                {
+                    LastModified = response.LastModified;
+                    StatusCode = response.StatusCode;
+                    StatusDescription = response.StatusDescription;
+                }
+                finally
+                {
+                    response.Close();
+                }
                 
-                //ResponseHeaders= response.Headers;
-
-                LastModified = response.LastModified;
-                StatusCode = response.StatusCode;
-                StatusDescription = response.StatusDescription;
-                response.Close();
 
                 return 0;
             }, actualRetries);