/// <summary> /// Wrap the specified stream in a <see cref="ProgressStream"/> that will report progress for reads. /// </summary> /// <param name="stream"> /// The stream to wrap. /// </param> /// <param name="total"> /// An optional total used to calculate progress. /// /// If not specified, the stream length is used (providing the stream supports seeking). /// </param> /// <param name="progressObserver"> /// An optional <see cref="IObserver{TValue}"/> that receives raw progress data. /// </param> /// <param name="ownsStream"> /// Should the <see cref="ProgressStream"/> close the inner stream when it is closed? /// </param> /// <returns> /// The new <see cref="ProgressStream"/>. /// </returns> /// <exception cref="InvalidOperationException"> /// The <paramref name="stream"/> is already a <see cref="ProgressStream"/>. /// <paramref name="total"/> was not specified, and the stream does not support seeking. /// </exception> public static ProgressStream WithReadProgress(this Stream stream, long?total = null, IObserver <RawProgressData <long> > progressObserver = null, bool ownsStream = true) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } IProgressSink <long> sink = DefaultSink.Int64(); if (total.HasValue) { sink.Total = total.Value; } else if (stream.CanSeek) { sink.Total = stream.Length; } else { throw new InvalidOperationException("Total was not specified, and cannot be auto-detected because the specified stream does not support seeking."); } if (progressObserver != null) { sink.Subscribe(progressObserver); } return(new ProgressStream(stream, StreamDirection.Read, ownsStream, sink)); }
/// <summary> /// Wrap the specified stream in a <see cref="ProgressStream"/> that will report progress for writes. /// </summary> /// <param name="stream"> /// The stream to wrap. /// </param> /// <param name="total"> /// The total used to calculate progress. /// </param> /// <param name="progressObserver"> /// An optional <see cref="IObserver{TValue}"/> that receives raw progress data. /// </param> /// <param name="ownsStream"> /// Should the <see cref="ProgressStream"/> close the inner stream when it is closed? /// </param> /// <returns> /// The new <see cref="ProgressStream"/>. /// </returns> /// <exception cref="InvalidOperationException"> /// The <paramref name="stream"/> is already a <see cref="ProgressStream"/>. /// </exception> public static ProgressStream WithWriteProgress(this Stream stream, long total, IObserver <RawProgressData <long> > progressObserver = null, bool ownsStream = true) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } IProgressSink <long> sink = DefaultSink.Int64(); if (progressObserver != null) { sink.Subscribe(progressObserver); } return(new ProgressStream(stream, StreamDirection.Write, ownsStream, sink)); }
/// <summary> /// Create new progress content. /// </summary> /// <param name="innerContent"> /// The inner <see cref="HttpContent"/>. /// </param> /// <param name="bufferSize"> /// An optional buffer size to use when transferring the inner content. /// /// Pass <c>null</c> to use the default buffer size. /// </param> /// <param name="sink"> /// The sink which will receive raw progress data. /// </param> public ProgressContent(HttpContent innerContent, int?bufferSize, IProgressSink <long> sink) { if (innerContent == null) { throw new ArgumentNullException(nameof(innerContent)); } if (sink == null) { throw new ArgumentNullException(nameof(sink)); } _innerContent = innerContent; _bufferSize = bufferSize; _sink = sink; LoadHeaders(); }
/// <summary> /// Create a new <see cref="ProgressStream"/> that wraps the specified inner <see cref="Stream"/>. /// </summary> /// <param name="innerStream"> /// The inner stream. /// </param> /// <param name="streamDirection"> /// The direction in which the stream's data is expected to flow. /// </param> /// <param name="ownsStream"> /// Should the <see cref="ProgressStream"/> close the inner stream when it is closed? /// </param> /// <param name="sink"> /// The sink used to report stream progress. /// </param> public ProgressStream(Stream innerStream, StreamDirection streamDirection, bool ownsStream, IProgressSink <long> sink) { if (innerStream == null) { throw new ArgumentNullException(nameof(innerStream)); } if (innerStream is ProgressStream) { throw new InvalidOperationException("Wrapping a ProgressStream2 in another ProgressStream2 is not currently supported."); } if (streamDirection == StreamDirection.Unknown) { throw new ArgumentOutOfRangeException(nameof(streamDirection), streamDirection, $"Invalid stream direction: '{streamDirection}'."); } _innerStream = innerStream; _streamDirection = streamDirection; _sink = sink; }
/// <summary> /// Add progress reporting for the specified response message. /// </summary> /// <param name="response"> /// The HTTP response message. /// </param> /// <param name="progressSink"> /// An optional sink that will receive raw progress data. /// </param> /// <param name="bufferSize"> /// An optional buffer size to use when transferring content. /// /// If not specified, the default buffer size is used. /// </param> /// <returns> /// The response message. /// </returns> public static HttpResponseMessage AddProgress(this HttpResponseMessage response, IProgressSink <long> progressSink = null, int?bufferSize = null) { if (response == null) { throw new ArgumentNullException(nameof(response)); } // No response content? No progress to report. // Might be worth creating a dummy sink that just emits 100% progress. if (response.Content == null) { return(response); } if (response.Content is ProgressContent) { throw new InvalidOperationException("The HTTP response message has already been configured to report progress."); } response.Content = new ProgressContent( innerContent: response.Content, sink: progressSink ?? DefaultSink.Int64(), bufferSize: bufferSize ); return(response); }
/// <summary> /// Add progress reporting for the specified request message. /// </summary> /// <param name="request"> /// The HTTP request message. /// </param> /// <param name="progressSink"> /// An optional sink that will receive raw progress data. /// </param> /// <param name="bufferSize"> /// An optional buffer size to use when transferring content. /// /// If not specified, the default buffer size is used. /// </param> /// <returns> /// The request message. /// </returns> public static HttpRequestMessage AddProgress(this HttpRequestMessage request, IProgressSink <long> progressSink = null, int?bufferSize = null) { if (request == null) { throw new ArgumentNullException(nameof(request)); } // No response content? No progress to report. if (request.Content == null) { return(request); } if (request.Content is ProgressContent) { throw new InvalidOperationException("The HTTP request message has already been configured to report progress."); } request.Content = new ProgressContent( innerContent: request.Content, sink: progressSink ?? DefaultSink.Int64(), bufferSize: bufferSize ); request.SetProgressContextId(ProgressContext.Current.Id); return(request); }
/// <summary> /// Add progress reporting to the HTTP response message. /// </summary> /// <param name="response"> /// A <see cref="Task{T}"/> that yields the response message. /// </param> /// <param name="sink"> /// An optional sink that will receive raw progress data. /// </param> /// <param name="bufferSize"> /// An optional buffer size to use when transferring content. /// /// If not specified, the default buffer size is used. /// </param> /// <returns> /// The response message. /// </returns> public static async Task <HttpResponseMessage> WithProgress(this Task <HttpResponseMessage> response, IProgressSink <long> sink, int?bufferSize = null) { if (response == null) { throw new ArgumentNullException(nameof(response)); } HttpResponseMessage responseMessage = null; try { responseMessage = await response; responseMessage.AddProgress(sink, bufferSize); } catch { using (responseMessage) { throw; } } return(responseMessage); }
/// <summary> /// Create new progress content. /// </summary> /// <param name="innerContent"> /// The inner <see cref="HttpContent"/>. /// </param> /// <param name="sink"> /// The sink which will receive raw progress data. /// </param> public ProgressContent(HttpContent innerContent, IProgressSink <long> sink) : this(innerContent, null, sink) { }