2010-08-20 10:45:09 +00:00
/ *
2014-02-25 15:31:10 +01:00
* Copyright ( c ) 2006 - 2014 , openmetaverse . org
2010-08-20 10:45:09 +00:00
* All rights reserved .
*
* - Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* - Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
* - Neither the name of the openmetaverse . org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Net ;
using System.Security.Cryptography.X509Certificates ;
using OpenMetaverse.Http ;
namespace OpenMetaverse
{
/// <summary>
/// Represends individual HTTP Download request
/// </summary>
public class DownloadRequest
{
/// <summary>URI of the item to fetch</summary>
public Uri Address ;
/// <summary>Timout specified in milliseconds</summary>
public int MillisecondsTimeout ;
/// <summary>Download progress callback</summary>
public CapsBase . DownloadProgressEventHandler DownloadProgressCallback ;
/// <summary>Download completed callback</summary>
public CapsBase . RequestCompletedEventHandler CompletedCallback ;
/// <summary>Accept the following content type</summary>
public string ContentType ;
2013-03-30 15:31:00 +01:00
/// <summary>How many times will this request be retried</summary>
public int Retries = 5 ;
/// <summary>Current fetch attempt</summary>
public int Attempt = 0 ;
2010-08-20 10:45:09 +00:00
/// <summary>Default constructor</summary>
public DownloadRequest ( )
{
}
/// <summary>Constructor</summary>
public DownloadRequest ( 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 ;
}
}
2011-07-09 11:32:09 +00:00
internal class ActiveDownload
{
public List < CapsBase . DownloadProgressEventHandler > ProgresHadlers = new List < CapsBase . DownloadProgressEventHandler > ( ) ;
public List < CapsBase . RequestCompletedEventHandler > CompletedHandlers = new List < CapsBase . RequestCompletedEventHandler > ( ) ;
public HttpWebRequest Request ;
}
2010-08-20 10:45:09 +00:00
/// <summary>
/// Manages async HTTP downloads with a limit on maximum
/// concurrent downloads
/// </summary>
public class DownloadManager
{
Queue < DownloadRequest > queue = new Queue < DownloadRequest > ( ) ;
2011-07-09 11:32:09 +00:00
Dictionary < string , ActiveDownload > activeDownloads = new Dictionary < string , ActiveDownload > ( ) ;
2010-08-20 10:45:09 +00:00
X509Certificate2 m_ClientCert ;
/// <summary>Maximum number of parallel downloads from a single endpoint</summary>
2014-08-11 23:57:33 +01:00
public int ParallelDownloads { get ; set ; }
2010-08-20 10:45:09 +00:00
/// <summary>Client certificate</summary>
public X509Certificate2 ClientCert
{
get { return m_ClientCert ; }
set { m_ClientCert = value ; }
}
/// <summary>Default constructor</summary>
public DownloadManager ( )
{
2014-10-28 23:43:44 +00:00
ParallelDownloads = 8 ;
2010-08-20 10:45:09 +00:00
}
/// <summary>Cleanup method</summary>
public virtual void Dispose ( )
{
lock ( activeDownloads )
{
2013-03-30 15:31:00 +01:00
foreach ( ActiveDownload download in activeDownloads . Values )
2010-08-20 10:45:09 +00:00
{
try
{
2011-07-09 11:32:09 +00:00
if ( download . Request ! = null )
{
download . Request . Abort ( ) ;
}
2010-08-20 10:45:09 +00:00
}
catch { }
}
2011-07-09 11:32:09 +00:00
activeDownloads . Clear ( ) ;
2010-08-20 10:45:09 +00:00
}
}
/// <summary>Setup http download request</summary>
protected virtual HttpWebRequest SetupRequest ( Uri address , string acceptHeader )
{
HttpWebRequest request = ( HttpWebRequest ) HttpWebRequest . Create ( address ) ;
request . Method = "GET" ;
if ( ! string . IsNullOrEmpty ( acceptHeader ) )
request . Accept = acceptHeader ;
// Add the client certificate to the request if one was given
if ( m_ClientCert ! = null )
request . ClientCertificates . Add ( m_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 ;
2014-08-11 23:57:33 +01:00
// Crank up the max number of connections per endpoint
if ( request . ServicePoint . ConnectionLimit < Settings . MAX_HTTP_CONNECTIONS )
{
Logger . Log ( string . Format ( "In DownloadManager.SetupRequest() setting conn limit for {0}:{1} to {2}" , address . Host , address . Port , Settings . MAX_HTTP_CONNECTIONS ) , Helpers . LogLevel . Debug ) ;
request . ServicePoint . ConnectionLimit = Settings . MAX_HTTP_CONNECTIONS ;
}
2010-08-20 10:45:09 +00:00
return request ;
}
/// <summary>Check the queue for pending work</summary>
private void EnqueuePending ( )
{
lock ( queue )
{
if ( queue . Count > 0 )
{
int nr = 0 ;
2011-07-09 11:32:09 +00:00
lock ( activeDownloads )
{
nr = activeDownloads . Count ;
}
2010-08-20 10:45:09 +00:00
2013-07-18 23:30:05 +02:00
// Logger.DebugLog(nr.ToString() + " active downloads. Queued textures: " + queue.Count.ToString());
2013-03-30 15:31:00 +01:00
2010-08-20 10:45:09 +00:00
for ( int i = nr ; i < ParallelDownloads & & queue . Count > 0 ; i + + )
{
DownloadRequest item = queue . Dequeue ( ) ;
2011-07-09 11:32:09 +00:00
lock ( activeDownloads )
{
string addr = item . Address . ToString ( ) ;
if ( activeDownloads . ContainsKey ( addr ) )
2010-08-20 10:45:09 +00:00
{
2011-07-09 11:32:09 +00:00
activeDownloads [ addr ] . CompletedHandlers . Add ( item . CompletedCallback ) ;
if ( item . DownloadProgressCallback ! = null )
{
activeDownloads [ addr ] . ProgresHadlers . Add ( item . DownloadProgressCallback ) ;
}
2010-08-20 10:45:09 +00:00
}
2011-07-09 11:32:09 +00:00
else
{
ActiveDownload activeDownload = new ActiveDownload ( ) ;
activeDownload . CompletedHandlers . Add ( item . CompletedCallback ) ;
if ( item . DownloadProgressCallback ! = null )
{
activeDownload . ProgresHadlers . Add ( item . DownloadProgressCallback ) ;
}
Logger . DebugLog ( "Requesting " + item . Address . ToString ( ) ) ;
activeDownload . Request = SetupRequest ( item . Address , item . ContentType ) ;
CapsBase . DownloadDataAsync (
activeDownload . Request ,
item . MillisecondsTimeout ,
( HttpWebRequest request , HttpWebResponse response , int bytesReceived , int totalBytesToReceive ) = >
{
foreach ( CapsBase . DownloadProgressEventHandler handler in activeDownload . ProgresHadlers )
{
handler ( request , response , bytesReceived , totalBytesToReceive ) ;
}
} ,
( HttpWebRequest request , HttpWebResponse response , byte [ ] responseData , Exception error ) = >
{
lock ( activeDownloads ) activeDownloads . Remove ( addr ) ;
2013-03-30 15:31:00 +01:00
if ( error = = null | | item . Attempt > = item . Retries | | ( error ! = null & & error . Message . Contains ( "404" ) ) )
{
foreach ( CapsBase . RequestCompletedEventHandler handler in activeDownload . CompletedHandlers )
{
handler ( request , response , responseData , error ) ;
}
}
else
2011-07-09 11:32:09 +00:00
{
2013-03-30 15:31:00 +01:00
item . Attempt + + ;
Logger . Log ( string . Format ( "Texture {0} HTTP download failed, trying again retry {1}/{2}" ,
item . Address , item . Attempt , item . Retries ) , Helpers . LogLevel . Warning ) ;
lock ( queue ) queue . Enqueue ( item ) ;
2011-07-09 11:32:09 +00:00
}
2013-03-30 15:31:00 +01:00
2011-07-09 11:32:09 +00:00
EnqueuePending ( ) ;
}
) ;
activeDownloads [ addr ] = activeDownload ;
}
}
2010-08-20 10:45:09 +00:00
}
}
}
}
2014-10-28 23:50:59 +00:00
/// <summary>Enqueue a new HTTP download</summary>
public void QueueDownload ( DownloadRequest req )
2010-08-20 10:45:09 +00:00
{
2011-07-09 11:32:09 +00:00
lock ( activeDownloads )
{
string addr = req . Address . ToString ( ) ;
if ( activeDownloads . ContainsKey ( addr ) )
{
activeDownloads [ addr ] . CompletedHandlers . Add ( req . CompletedCallback ) ;
if ( req . DownloadProgressCallback ! = null )
{
activeDownloads [ addr ] . ProgresHadlers . Add ( req . DownloadProgressCallback ) ;
}
return ;
}
}
2010-08-20 10:45:09 +00:00
lock ( queue )
{
queue . Enqueue ( req ) ;
}
EnqueuePending ( ) ;
}
}
}