private DownloadConnection SpawnConnection(StreamVolume stream) { var connection = new DownloadConnection(Request, new DownloadRange(stream.Range.Start), stream, 0); connection.Completed += delegate(object sender, CompletedEventArgs args) { connection.Stream.Close(); OnProgressChanged(); }; connection.ProgressChanged += delegate(object sender, EventArgs args) { OnProgressChanged(); }; connection.ResponseReceived += delegate(object sender, ResponseReceivedEventArgs args) { if (args.Response.SupportsRange) { connection.Range = connection.Range.ShrinkLength(stream.Range.Length); } }; lock (_connections) { _connections.Add(connection); } return(connection); }
public StreamVolume GetFreeVolume(long start, long maximumLength) { if (start >= TotalSize) { throw new ArgumentException("Start position is greater or equal to the total length.", nameof(start)); } if (maximumLength == -1) { maximumLength = TotalSize - start; } var volumes = Volumes.ToList(); StreamVolume overlap; do { overlap = volumes.FirstOrDefault(volume => volume.Range.Contains(start)); if (overlap != null) { start = overlap.Range.End + 1; } } while (overlap != null); var end = Math.Min(start + maximumLength, TotalSize) - 1; do { overlap = volumes.FirstOrDefault(volume => volume.Range.Contains(end)); if (overlap != null) { end = overlap.Range.Start - 1; } } while (overlap != null); if (end >= start) { var volume = new StreamVolume(Stream, new StreamVolumeRange(start, end)); lock (_volumes) { _volumes.Add(volume); } return(volume); } return(null); }
public async Task Start(CancellationToken token) { var partSize = 1024 * 1024 * 3; var nConnections = 3; var splitPartition = new StreamVolume(Partition.Stream, StreamVolumeRange.Empty); var initialConnection = SpawnConnection(splitPartition); var taskSource = new TaskCompletionSource <DownloadResponse>(); initialConnection.ResponseReceived += (sender, args) => { if (OnResponseReceived(args.Response)) { args.Abort = true; taskSource.SetCanceled(); return; } if (args.Response.SupportsRange) { Partition.TotalSize = args.Response.FileSize; Partition.Stream.SetLength(Partition.TotalSize); var newRange = Partition.GetFreeVolume(0, partSize); splitPartition.Range = newRange.Range; initialConnection.Range = newRange.Range; } taskSource.SetResult(args.Response); }; var initialTask = initialConnection.Start(token); var response = await taskSource.Task; if (!response.SupportsRange) { await Task.WhenAll(initialTask); if (initialTask.IsCanceled) { // Canceled } return; } var connections = new List <Tuple <DownloadConnection, Task> > { new Tuple <DownloadConnection, Task>(initialConnection, initialTask) }; StreamVolume range; do { range = Partition.GetFreeVolume(0, partSize); if (range != null) { while (connections.Count(t => t.Item1.Status == DownloadConnectionStatus.Downloading) >= nConnections) { await Task.WhenAny( connections.Where(t => t.Item1.Status == DownloadConnectionStatus.Downloading) .Select(t => t.Item2) .ToArray()); } var connection = SpawnConnection(range); var task = connection.Start(token); connections.Add(new Tuple <DownloadConnection, Task>(connection, task)); } } while (!token.IsCancellationRequested && (range != null)); await Task.WhenAll(connections.Select(t => t.Item2).ToArray()); }