Revision 692ec33b
b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs | ||
---|---|---|
131 | 131 |
private async Task StartMonitoring() |
132 | 132 |
{ |
133 | 133 |
try |
134 |
{ |
|
134 |
{ |
|
135 |
var accounts = Settings.Accounts.Select(MonitorAccount); |
|
136 |
await TaskEx.WhenAll(accounts); |
|
137 |
_statusService = StatusService.Start(); |
|
138 |
|
|
139 |
/* |
|
135 | 140 |
foreach (var account in Settings.Accounts) |
136 | 141 |
{ |
137 | 142 |
await MonitorAccount(account); |
138 | 143 |
} |
139 |
_statusService = StatusService.Start(); |
|
144 |
*/ |
|
145 |
|
|
140 | 146 |
} |
141 | 147 |
catch (AggregateException exc) |
142 | 148 |
{ |
b/trunk/Pithos.Core/Agents/NetworkAgent.cs | ||
---|---|---|
478 | 478 |
var fileAgent = GetFileAgent(accountInfo); |
479 | 479 |
foreach (var trashObject in trashObjects) |
480 | 480 |
{ |
481 |
var relativePath = trashObject.RelativeUrlToFilePath(accountInfo.UserName); |
|
482 |
//and remove any matching objects from the list, adding them to the commonObjects list |
|
481 |
var barePath = trashObject.RelativeUrlToFilePath(accountInfo.UserName); |
|
482 |
//HACK: Assume only the "pithos" container is used. Must find out what happens when |
|
483 |
//deleting a file from a different container |
|
484 |
var relativePath = Path.Combine("pithos", barePath); |
|
483 | 485 |
fileAgent.Delete(relativePath); |
484 | 486 |
} |
485 | 487 |
} |
... | ... | |
747 | 749 |
try |
748 | 750 |
{ |
749 | 751 |
if (action == null) |
750 |
throw new ArgumentNullException("action");
|
|
752 |
throw new ArgumentNullException("action"); |
|
751 | 753 |
Contract.EndContractBlock(); |
752 | 754 |
|
753 |
var accountInfo=action.AccountInfo;
|
|
754 |
|
|
755 |
var fileInfo=action.LocalFile;
|
|
755 |
var accountInfo = action.AccountInfo;
|
|
756 |
|
|
757 |
var fileInfo = action.LocalFile;
|
|
756 | 758 |
|
757 | 759 |
if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase)) |
758 | 760 |
return; |
... | ... | |
760 | 762 |
var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath); |
761 | 763 |
if (relativePath.StartsWith(FolderConstants.OthersFolder)) |
762 | 764 |
{ |
763 |
var parts=relativePath.Split('\\');
|
|
765 |
var parts = relativePath.Split('\\');
|
|
764 | 766 |
var accountName = parts[1]; |
765 | 767 |
var oldName = accountInfo.UserName; |
766 | 768 |
var absoluteUri = accountInfo.StorageUri.AbsoluteUri; |
767 |
var nameIndex=absoluteUri.IndexOf(oldName);
|
|
768 |
var root=absoluteUri.Substring(0, nameIndex);
|
|
769 |
var nameIndex = absoluteUri.IndexOf(oldName);
|
|
770 |
var root = absoluteUri.Substring(0, nameIndex);
|
|
769 | 771 |
|
770 | 772 |
accountInfo = new AccountInfo |
771 | 773 |
{ |
772 | 774 |
UserName = accountName, |
773 | 775 |
AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]), |
774 | 776 |
StorageUri = new Uri(root + accountName), |
775 |
BlockHash=accountInfo.BlockHash,
|
|
776 |
BlockSize=accountInfo.BlockSize,
|
|
777 |
Token=accountInfo.Token
|
|
777 |
BlockHash = accountInfo.BlockHash,
|
|
778 |
BlockSize = accountInfo.BlockSize,
|
|
779 |
Token = accountInfo.Token
|
|
778 | 780 |
}; |
779 | 781 |
} |
780 | 782 |
|
781 | 783 |
|
782 | 784 |
var fullFileName = fileInfo.FullName; |
783 |
using(var gate=NetworkGate.Acquire(fullFileName,NetworkOperation.Uploading))
|
|
785 |
using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
|
|
784 | 786 |
{ |
785 | 787 |
//Abort if the file is already being uploaded or downloaded |
786 | 788 |
if (gate.Failed) |
... | ... | |
791 | 793 |
|
792 | 794 |
var client = new CloudFilesClient(accountInfo); |
793 | 795 |
//Even if GetObjectInfo times out, we can proceed with the upload |
794 |
var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
|
|
796 |
var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name); |
|
795 | 797 |
var cloudHash = info.Hash.ToLower(); |
796 | 798 |
|
797 | 799 |
var hash = action.LocalHash.Value; |
798 | 800 |
var topHash = action.TopHash.Value; |
799 | 801 |
|
800 | 802 |
//If the file hashes match, abort the upload |
801 |
if (hash == cloudHash || topHash ==cloudHash)
|
|
803 |
if (hash == cloudHash || topHash == cloudHash)
|
|
802 | 804 |
{ |
803 | 805 |
//but store any metadata changes |
804 | 806 |
this.StatusKeeper.StoreInfo(fullFileName, info); |
... | ... | |
806 | 808 |
return; |
807 | 809 |
} |
808 | 810 |
|
809 |
if (info.AllowedTo=="read")
|
|
811 |
if (info.AllowedTo == "read")
|
|
810 | 812 |
return; |
811 | 813 |
|
812 | 814 |
//Mark the file as modified while we upload it |
... | ... | |
818 | 820 |
|
819 | 821 |
//First, calculate the tree hash |
820 | 822 |
var treeHash = await Signature.CalculateTreeHashAsync(fileInfo.FullName, accountInfo.BlockSize, |
821 |
accountInfo.BlockHash);
|
|
822 |
|
|
823 |
await UploadWithHashMap(accountInfo,cloudFile,fileInfo,cloudFile.Name,treeHash);
|
|
823 |
accountInfo.BlockHash); |
|
824 |
|
|
825 |
await UploadWithHashMap(accountInfo, cloudFile, fileInfo, cloudFile.Name, treeHash);
|
|
824 | 826 |
|
825 | 827 |
//If everything succeeds, change the file and overlay status to normal |
826 | 828 |
this.StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal); |
... | ... | |
834 | 836 |
var exc = ex.InnerException as WebException; |
835 | 837 |
if (exc == null) |
836 | 838 |
throw ex.InnerException; |
837 |
var response = exc.Response as HttpWebResponse; |
|
838 |
if (response == null) |
|
839 |
throw exc; |
|
840 |
if (response.StatusCode == HttpStatusCode.Unauthorized) |
|
841 |
{ |
|
842 |
Log.Error("Not allowed to upload file", exc); |
|
843 |
var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName); |
|
844 |
StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal); |
|
845 |
StatusNotification.NotifyChange(message, TraceLevel.Warning); |
|
839 |
if (HandleUploadWebException(action, exc)) |
|
846 | 840 |
return; |
847 |
} |
|
841 |
throw; |
|
842 |
} |
|
843 |
catch (WebException ex) |
|
844 |
{ |
|
845 |
if (HandleUploadWebException(action, ex)) |
|
846 |
return; |
|
847 |
throw; |
|
848 |
} |
|
849 |
catch (Exception ex) |
|
850 |
{ |
|
851 |
Log.Error("Unexpected error while uploading file", ex); |
|
848 | 852 |
throw; |
849 | 853 |
} |
850 | 854 |
|
851 | 855 |
} |
852 | 856 |
|
857 |
private bool HandleUploadWebException(CloudAction action, WebException exc) |
|
858 |
{ |
|
859 |
var response = exc.Response as HttpWebResponse; |
|
860 |
if (response == null) |
|
861 |
throw exc; |
|
862 |
if (response.StatusCode == HttpStatusCode.Unauthorized) |
|
863 |
{ |
|
864 |
Log.Error("Not allowed to upload file", exc); |
|
865 |
var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName); |
|
866 |
StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal); |
|
867 |
StatusNotification.NotifyChange(message, TraceLevel.Warning); |
|
868 |
return true; |
|
869 |
} |
|
870 |
return false; |
|
871 |
} |
|
872 |
|
|
853 | 873 |
public async Task UploadWithHashMap(AccountInfo accountInfo,ObjectInfo cloudFile,FileInfo fileInfo,string url,TreeHash treeHash) |
854 | 874 |
{ |
855 | 875 |
if (accountInfo == null) |
b/trunk/Pithos.Network/CloudFilesClient.cs | ||
---|---|---|
7 | 7 |
using System.Collections.Generic; |
8 | 8 |
using System.Collections.Specialized; |
9 | 9 |
using System.ComponentModel.Composition; |
10 |
using System.Diagnostics; |
|
10 | 11 |
using System.Diagnostics.Contracts; |
11 | 12 |
using System.IO; |
12 | 13 |
using System.Linq; |
... | ... | |
946 | 947 |
|
947 | 948 |
|
948 | 949 |
//Don't use a timeout because putting the hashmap may be a long process |
949 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
|
950 |
var client = new RestClient(_baseClient) { Timeout = 0 };
|
|
950 | 951 |
if (!String.IsNullOrWhiteSpace(account)) |
951 | 952 |
client.BaseAddress = GetAccountUrl(account); |
952 | 953 |
|
... | ... | |
959 | 960 |
|
960 | 961 |
//Send the tree hash as Json to the server |
961 | 962 |
client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream"; |
962 |
var uploadTask=client.UploadStringTask(uri, "PUT", hash.ToJson());
|
|
963 |
|
|
963 |
var jsonHash = hash.ToJson();
|
|
964 |
var uploadTask=client.UploadStringTask(uri, "PUT", jsonHash); |
|
964 | 965 |
|
965 | 966 |
return uploadTask.ContinueWith(t => |
966 | 967 |
{ |
... | ... | |
987 | 988 |
using (var stream = response.GetResponseStream()) |
988 | 989 |
using(var reader=new StreamReader(stream)) |
989 | 990 |
{ |
991 |
Debug.Assert(stream.Position == 0); |
|
990 | 992 |
//We need to cleanup the content before returning it because it contains |
991 | 993 |
//error content after the list of hashes |
992 | 994 |
var hashes = new List<string>(); |
b/trunk/Pithos.Network/RestClient.cs | ||
---|---|---|
85 | 85 |
} |
86 | 86 |
|
87 | 87 |
|
88 |
private WebHeaderCollection _responseHeaders; |
|
89 |
|
|
90 |
public new WebHeaderCollection ResponseHeaders |
|
91 |
{ |
|
92 |
get |
|
93 |
{ |
|
94 |
if (base.ResponseHeaders==null) |
|
95 |
{ |
|
96 |
return _responseHeaders; |
|
97 |
} |
|
98 |
else |
|
99 |
{ |
|
100 |
_responseHeaders = null; |
|
101 |
return base.ResponseHeaders; |
|
102 |
} |
|
103 |
|
|
104 |
} |
|
105 |
|
|
106 |
set { _responseHeaders = value; } |
|
107 |
} |
|
108 | 88 |
protected override WebRequest GetWebRequest(Uri address) |
109 | 89 |
{ |
110 | 90 |
TimedOut = false; |
... | ... | |
187 | 167 |
//Does the response have any content to log? |
188 | 168 |
if (exc.Response.ContentLength > 0) |
189 | 169 |
{ |
190 |
var content = GetContent(exc.Response);
|
|
170 |
var content = LogContent(exc.Response);
|
|
191 | 171 |
Log.ErrorFormat(content); |
192 | 172 |
} |
193 | 173 |
return false; |
... | ... | |
205 | 185 |
|
206 | 186 |
public DateTime LastModified { get; private set; } |
207 | 187 |
|
208 |
private static string GetContent(WebResponse webResponse)
|
|
188 |
private static string LogContent(WebResponse webResponse)
|
|
209 | 189 |
{ |
210 | 190 |
if (webResponse == null) |
211 | 191 |
throw new ArgumentNullException("webResponse"); |
212 | 192 |
Contract.EndContractBlock(); |
213 | 193 |
|
214 |
string content; |
|
215 |
using (var stream = webResponse.GetResponseStream()) |
|
216 |
using (var reader = new StreamReader(stream)) |
|
194 |
//The response stream must be copied to avoid affecting other code by disposing of the |
|
195 |
//original response stream. |
|
196 |
var stream = webResponse.GetResponseStream(); |
|
197 |
using(var memStream=new MemoryStream((int) stream.Length)) |
|
198 |
using (var reader = new StreamReader(memStream)) |
|
217 | 199 |
{ |
218 |
content = reader.ReadToEnd(); |
|
200 |
stream.CopyTo(memStream); |
|
201 |
string content = reader.ReadToEnd(); |
|
202 |
|
|
203 |
stream.Seek(0,SeekOrigin.Begin); |
|
204 |
return content; |
|
219 | 205 |
} |
220 |
return content; |
|
221 | 206 |
} |
222 | 207 |
|
223 | 208 |
public string DownloadStringWithRetry(string address,int retries=0) |
... | ... | |
232 | 217 |
|
233 | 218 |
var actualRetries = (retries == 0) ? Retries : retries; |
234 | 219 |
|
235 |
|
|
220 |
var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress); |
|
221 |
|
|
236 | 222 |
var task = Retry(() => |
237 |
{ |
|
238 |
var uriString = String.Join("/", BaseAddress.TrimEnd('/'), actualAddress); |
|
223 |
{ |
|
239 | 224 |
var content = base.DownloadString(uriString); |
240 | 225 |
|
241 | 226 |
if (StatusCode == HttpStatusCode.NoContent) |
... | ... | |
308 | 293 |
if (method == "PUT") |
309 | 294 |
request.ContentLength = 0; |
310 | 295 |
|
296 |
//Have to use try/finally instead of using here, because WebClient needs a valid WebResponse object |
|
297 |
//in order to return response headers |
|
311 | 298 |
var response = (HttpWebResponse)GetWebResponse(request); |
312 |
//var response = (HttpWebResponse)request.GetResponse(); |
|
299 |
try |
|
300 |
{ |
|
301 |
LastModified = response.LastModified; |
|
302 |
StatusCode = response.StatusCode; |
|
303 |
StatusDescription = response.StatusDescription; |
|
304 |
} |
|
305 |
finally |
|
306 |
{ |
|
307 |
response.Close(); |
|
308 |
} |
|
313 | 309 |
|
314 |
//ResponseHeaders= response.Headers; |
|
315 |
|
|
316 |
LastModified = response.LastModified; |
|
317 |
StatusCode = response.StatusCode; |
|
318 |
StatusDescription = response.StatusDescription; |
|
319 |
response.Close(); |
|
320 | 310 |
|
321 | 311 |
return 0; |
322 | 312 |
}, actualRetries); |
Also available in: Unified diff