/// <summary> /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>. /// </summary> /// <param name="notification">The payload reference.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> public virtual async Task <Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(notification, "notification"); byte[] messageBuffer = null; const int MaxAttempts = 2; int retry; Exception exceptionLeadingToRetry = null; for (retry = 0; retry < MaxAttempts; retry++) { exceptionLeadingToRetry = null; try { var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken).ConfigureAwait(false); messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false); // Calculate hash of downloaded message and check that it matches the referenced message hash. if (!this.CryptoServices.IsHashMatchWithTolerantHashAlgorithm(messageBuffer, notification.Hash, CryptoProviderExtensions.ParseHashAlgorithmName(notification.HashAlgorithmName))) { // Sometimes when the hash mismatches, it's because the download was truncated // (from switching out the application and then switching back, for example). // Check for that before throwing. if (responseMessage.Content.Headers.ContentLength.HasValue && responseMessage.Content.Headers.ContentLength.Value > messageBuffer.Length) { // It looks like the message was truncated. Retry. exceptionLeadingToRetry = new InvalidMessageException(); continue; } throw new InvalidMessageException(); } // Stop retrying. We got something that worked! break; } catch (HttpRequestException ex) { exceptionLeadingToRetry = ex; continue; } } if (exceptionLeadingToRetry != null) { if (exceptionLeadingToRetry.StackTrace != null) { ExceptionDispatchInfo.Capture(exceptionLeadingToRetry).Throw(); } else { throw exceptionLeadingToRetry; } } var encryptionVariables = new SymmetricEncryptionVariables(notification.Key, notification.IV); var cipherStream = new MemoryStream(messageBuffer); var plainTextStream = new MemoryStream(); await this.CryptoServices.DecryptAsync(cipherStream, plainTextStream, encryptionVariables, cancellationToken).ConfigureAwait(false); plainTextStream.Position = 0; var plainTextReader = new BinaryReader(plainTextStream); var message = Utilities.DeserializeDataContract <Payload>(plainTextReader); message.PayloadReferenceUri = notification.ReferenceLocation; return(message); }
/// <summary> /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>. /// </summary> /// <param name="notification">The payload reference.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> public virtual async Task<Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(notification, "notification"); byte[] messageBuffer = null; const int MaxAttempts = 2; int retry; Exception exceptionLeadingToRetry = null; for (retry = 0; retry < MaxAttempts; retry++) { exceptionLeadingToRetry = null; try { var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken).ConfigureAwait(false); messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false); // Calculate hash of downloaded message and check that it matches the referenced message hash. if (!this.CryptoServices.IsHashMatchWithTolerantHashAlgorithm(messageBuffer, notification.Hash, CryptoProviderExtensions.ParseHashAlgorithmName(notification.HashAlgorithmName))) { // Sometimes when the hash mismatches, it's because the download was truncated // (from switching out the application and then switching back, for example). // Check for that before throwing. if (responseMessage.Content.Headers.ContentLength.HasValue && responseMessage.Content.Headers.ContentLength.Value > messageBuffer.Length) { // It looks like the message was truncated. Retry. exceptionLeadingToRetry = new InvalidMessageException(); continue; } throw new InvalidMessageException(); } // Stop retrying. We got something that worked! break; } catch (HttpRequestException ex) { exceptionLeadingToRetry = ex; continue; } } if (exceptionLeadingToRetry != null) { if (exceptionLeadingToRetry.StackTrace != null) { ExceptionDispatchInfo.Capture(exceptionLeadingToRetry).Throw(); } else { throw exceptionLeadingToRetry; } } var encryptionVariables = new SymmetricEncryptionVariables(notification.Key, notification.IV); var cipherStream = new MemoryStream(messageBuffer); var plainTextStream = new MemoryStream(); await this.CryptoServices.DecryptAsync(cipherStream, plainTextStream, encryptionVariables, cancellationToken).ConfigureAwait(false); plainTextStream.Position = 0; var plainTextReader = new BinaryReader(plainTextStream); var message = Utilities.DeserializeDataContract<Payload>(plainTextReader); message.PayloadReferenceUri = notification.ReferenceLocation; return message; }