private async Task <GetFileResult> ReadFileFromLocalAsync() { return(await Task.Run(delegate { GetFileResult getFileResult = new GetFileResult { Code = HttpStatusCode.Unused }; try { getFileResult.RespStream = fileReader.ReadFile(); getFileResult.Code = HttpStatusCode.OK; return getFileResult; } catch (ArgumentException) { getFileResult.ErrorMessage = "File path contains invalid characters"; return getFileResult; } catch (IOException) { getFileResult.ErrorMessage = "IO exception reading file"; return getFileResult; } catch (UnauthorizedAccessException) { getFileResult.ErrorMessage = "Could not access file for reading"; return getFileResult; } }).ConfigureAwait(false)); }
private bool IsStale(GetFileResult fileResult) { if (fileResult.IsFromCache && fileResult.AgeSeconds.HasValue) { return(fileResult.AgeSeconds <= PollingIntervalMins * 60); } return(false); }
/// <summary> /// Get a file and send telemetry events. /// </summary> /// <param name="fromServer"></param> /// <returns></returns> private async Task <GetFileResult> GetFileAndInstrumentAsync(bool fromServer = false) { GetFileResult getFileResult = await(fromServer ? requestor.GetFileFromServerAsync() : requestor.GetFileFromCacheAsync()).ConfigureAwait(false); if (fromServer && (!getFileResult.IsFromCache || !getFileResult.IsSuccessStatusCode)) { InstrumentGetFile(getFileResult); } return(getFileResult); }
/// <summary> /// Gets the elapsed time (in seconds) since the last error in downloading / revalidating the file from the /// server. /// </summary> /// <returns>Time in seconds since last error or Int.MaxValue if no error has ever occured.</returns> async Task <int> IRemoteControlHTTPRequestor.LastServerRequestErrorSecondsAgoAsync() { using (GetFileResult getFileResult = await GetFile(errorMarkerFileUrl, httpRequestTimeoutMillis, CacheOnlyPolicy).ConfigureAwait(false)) { if (getFileResult.IsFromCache) { return(getFileResult.AgeSeconds.Value); } return(int.MaxValue); } }
/// <summary> /// Reads the file from the server url. /// In case of errors reading the file from the server, returned <see cref="T:Coding4Fun.VisualStudio.RemoteControl.GetFileResult" /> object's /// IsSuccessStatusCode value will be false. /// </summary> /// <returns>Information about the file obtained from the server</returns> async Task <GetFileResult> IRemoteControlHTTPRequestor.GetFileFromServerAsync() { GetFileResult result = new GetFileResult { Code = HttpStatusCode.Unused }; try { int i = 0; while (true) { if (i >= 2) { return(result); } if (i > 0) { int num = Math.Min(32, (int)Math.Pow(2.0, i)); try { await Task.Delay(num * 1000, cancellationTokenSource.Token).ConfigureAwait(false); } catch (OperationCanceledException) { return(result); } catch (ObjectDisposedException) { return(result); } result.Dispose(); } result = await GetFile(url, httpRequestTimeoutMillis, ServerRevalidatePolicy).ConfigureAwait(false); if (result.IsSuccessStatusCode) { break; } i++; } return(result); } finally { if (!result.IsSuccessStatusCode && Platform.IsWindows) { WinINetHelper.WriteErrorResponseToCache(errorMarkerFileUrl, result.Code); } } }
/// <summary> /// Instruments the usage of GetFileFromCache and GetFileFromServer. /// </summary> /// <param name="fileResult"></param> private void InstrumentGetFile(GetFileResult fileResult) { Dictionary <string, object> dictionary = new Dictionary <string, object> { { "VS.RemoteControl.DownloadFile.FullUrl", FullUrl }, { "VS.RemoteControl.DownloadFile.IsSuccess", fileResult.IsSuccessStatusCode }, { "VS.RemoteControl.DownloadFile.HttpRequestTimeoutInSecs", httpRequestTimeoutSeconds }, { "VS.RemoteControl.DownloadFile.IsFromCache", fileResult.IsFromCache }, { "VS.RemoteControl.DownloadFile.PollingIntervalInMins", PollingIntervalMins } }; Dictionary <string, object> dictionary2 = new Dictionary <string, object>(); if (fileResult.IsSuccessStatusCode) { dictionary.Add("VS.RemoteControl.DownloadFile.IsNotFound", fileResult.Code == HttpStatusCode.NotFound); if (fileResult.RespStream != null) { dictionary.Add("VS.RemoteControl.DownloadFile.StreamSize", fileResult.RespStream.Length); } if (fileResult.AgeSeconds.HasValue) { dictionary.Add("VS.RemoteControl.DownloadFile.AgeInSecs", fileResult.AgeSeconds.Value); } } else { if (fileResult.Code != HttpStatusCode.Unused) { dictionary.Add("VS.RemoteControl.DownloadFile.ErrorCode", Enum.GetName(typeof(HttpStatusCode), fileResult.Code)); } dictionary2.Add("VS.RemoteControl.DownloadFile.ErrorMessage", fileResult.ErrorMessage); } TelemetryLogger2("VS/RemoteControl/DownloadFile", dictionary, dictionary2); }
/// <summary> /// Determines if a local IE cache copy of the file is up-to-date. If no cached copy is available or the /// cached copy is not up-to-date, a request is made to the server to download or revalidate the file. The /// result of the server request is cached. /// </summary> /// <param name="cancellationToken">Cancellation token to cancel waiting of operation</param> /// <returns>Returns True if the copy in the IE cache is up-to-date by the end of the method. False in case of /// failures that prevent updating file.</returns> private async Task <bool> EnsureFileIsUpToDateAsync(CancellationToken cancellationToken) { if (isDisabled) { return(false); } try { await updateMutex.WaitAsync(cancellationToken).ConfigureAwait(false); for (int i = 1; i <= 2; i++) { using (GetFileResult getFileResult = await GetFileAndInstrumentAsync().ConfigureAwait(false)) { if (IsStale(getFileResult)) { return(getFileResult.IsSuccessStatusCode); } } if (await requestor.LastServerRequestErrorSecondsAgoAsync().ConfigureAwait(false) < PollingIntervalMins * 60) { return(false); } if (i < 2) { await Task.Delay(rand.Next(0, maxRandomDownloadDelaySeconds), cancellationToken).ConfigureAwait(false); } } using (GetFileResult getFileResult2 = await GetFileAndInstrumentAsync(true).ConfigureAwait(false)) { return(getFileResult2.IsSuccessStatusCode); } } finally { try { updateMutex.Release(); } catch (SemaphoreFullException) { } } }
/// <summary> /// Reads the settings file based on the <paramref name="staleBehavior" /> specified. This is the Async version /// of ReadFile method. /// </summary> /// <param name="staleBehavior">See <see cref="T:Coding4Fun.VisualStudio.RemoteControl.BehaviorOnStale" /> for details about each possible setting. /// In most cases use the BehaviorOnStale.ReturnStale setting.</param> /// <returns>A Stream that can be used to read the setting file. !Callers must call Dispose on this stream /// object returned. Or Null is returned in case of failure to get the file (or if server returned /// NotFound).</returns> public async Task <Stream> ReadFileAsync(BehaviorOnStale staleBehavior) { if (isDisabled) { return(null); } if (isDisposed) { throw new ObjectDisposedException("RemoteControlClient"); } if (Uri.IsLocalFile) { return((await ReadFileFromLocalAsync().ConfigureAwait(false)).RespStream); } switch (staleBehavior) { case BehaviorOnStale.ReturnStale: return((await GetFileAndInstrumentAsync().ConfigureAwait(false)).RespStream); case BehaviorOnStale.ReturnNull: { GetFileResult getFileResult = await GetFileAndInstrumentAsync().ConfigureAwait(false); if (IsStale(getFileResult)) { return(getFileResult.RespStream); } getFileResult.Dispose(); return(null); } case BehaviorOnStale.ForceDownload: return((await GetFileAndInstrumentAsync(true).ConfigureAwait(false)).RespStream); default: return(null); } }