Files
RaindropViewer/Assets/Raindrop/UI/map/network/ParallelDownloader.cs
T

159 lines
6.2 KiB
C#

using OpenMetaverse.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace Raindrop.Network
{
// why is the code so repeated!!!!
// parallel downloader for http maps tiles.
public class ParallelDownloader : IDisposable
{
Queue<QueuedItem> queue = new Queue<QueuedItem>();
List<HttpWebRequest> activeDownloads = new List<HttpWebRequest>();
public int ParallelDownloads { get; set; } = 15;
public X509Certificate2 ClientCert { get; set; }
public virtual void Dispose()
{
lock (activeDownloads)
{
foreach (var download in activeDownloads)
{
try
{
download.Abort();
}
catch { }
}
}
}
protected virtual HttpWebRequest SetupRequest(Uri address, string acceptHeader)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
request.Method = "GET";
if (!string.IsNullOrEmpty(acceptHeader))
request.Accept = acceptHeader;
// Add the client certificate to the request if one was given
if (ClientCert != null)
request.ClientCertificates.Add(ClientCert);
// Leave idle connections to this endpoint open for up to 60 seconds
request.ServicePoint.MaxIdleTime = 0;
// Disable stupid Expect-100: Continue header
request.ServicePoint.Expect100Continue = false;
// Crank up the max number of connections per endpoint (default is 2!)
request.ServicePoint.ConnectionLimit = Math.Max(request.ServicePoint.ConnectionLimit, 128);
// Caps requests are never sent as trickles of data, so Nagle's
// coalescing algorithm won't help us
request.ServicePoint.UseNagleAlgorithm = false;
return request;
}
private void EnqueuePending()
{
lock (queue)
{
if (queue.Count > 0)
{
int nr = 0;
lock (activeDownloads) nr = activeDownloads.Count;
for (int i = nr; i < ParallelDownloads && queue.Count > 0; i++)
{
QueuedItem item = queue.Dequeue(); //checlk this
var req = CapsBase.GetStringAsync(
item.address,
null,
item.millisecondsTimeout,
item.downloadProgressCallback,
(request, response, responseData, error) =>
{
lock (activeDownloads) activeDownloads.Remove(request);
item.completedCallback(request, response, responseData, error);
EnqueuePending();
OpenMetaverse.Logger.Log("download maptile completed",
OpenMetaverse.Helpers.LogLevel.Debug);
}
);
//Why the fuck the following snippet, which was the original implementaion in radegast, causes the main thread to stall on the instruction
// " IAsyncResult result = request.BeginGetResponse(GetResponse, state); " in CapsBase.cs line 110 (call to DownloadDataAsync() ).
// like really why???
//Debug.Log("Requesting " + item.address);
//HttpWebRequest req = SetupRequest(item.address, item.contentType);
//CapsBase.DownloadDataAsync(
// req,
// item.millisecondsTimeout,
// item.downloadProgressCallback,
// (request, response, responseData, error) =>
// {
// lock (activeDownloads) activeDownloads.Remove(request);
// item.completedCallback(request, response, responseData, error);
// EnqueuePending();
// }
//);
lock (activeDownloads) activeDownloads.Add(req); // this line?
}
}
}
}
// Call this to request content which runs a desired callback on completion.
public void QueueDownlad(Uri address, int millisecondsTimeout,
string contentType,
CapsBase.DownloadProgressEventHandler downloadProgressCallback,
CapsBase.RequestCompletedEventHandler completedCallback)
{
lock (queue)
{
queue.Enqueue(new QueuedItem(
address,
millisecondsTimeout,
contentType,
downloadProgressCallback,
completedCallback
));
}
EnqueuePending();
}
public class QueuedItem
{
public Uri address;
public int millisecondsTimeout;
public CapsBase.DownloadProgressEventHandler downloadProgressCallback;
public CapsBase.RequestCompletedEventHandler completedCallback;
public string contentType;
public QueuedItem(Uri address, int millisecondsTimeout,
string contentType,
CapsBase.DownloadProgressEventHandler downloadProgressCallback,
CapsBase.RequestCompletedEventHandler completedCallback)
{
this.address = address;
this.millisecondsTimeout = millisecondsTimeout;
this.downloadProgressCallback = downloadProgressCallback;
this.completedCallback = completedCallback;
this.contentType = contentType;
}
}
}
}