corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26 using System;
27 using System.Collections.Generic;
28 using System.Linq;
29 using System.Text;
30 using System.Net;
31 using System.Security.Cryptography.X509Certificates;
32 using OpenMetaverse.Http;
33  
34 namespace OpenMetaverse
35 {
36 /// <summary>
37 /// Represends individual HTTP Download request
38 /// </summary>
39 public class DownloadRequest
40 {
41 /// <summary>URI of the item to fetch</summary>
42 public Uri Address;
43 /// <summary>Timout specified in milliseconds</summary>
44 public int MillisecondsTimeout;
45 /// <summary>Download progress callback</summary>
46 public CapsBase.DownloadProgressEventHandler DownloadProgressCallback;
47 /// <summary>Download completed callback</summary>
48 public CapsBase.RequestCompletedEventHandler CompletedCallback;
49 /// <summary>Accept the following content type</summary>
50 public string ContentType;
51 /// <summary>How many times will this request be retried</summary>
52 public int Retries = 5;
53 /// <summary>Current fetch attempt</summary>
54 public int Attempt = 0;
55  
56 /// <summary>Default constructor</summary>
57 public DownloadRequest()
58 {
59 }
60  
61 /// <summary>Constructor</summary>
62 public DownloadRequest(Uri address, int millisecondsTimeout,
63 string contentType,
64 CapsBase.DownloadProgressEventHandler downloadProgressCallback,
65 CapsBase.RequestCompletedEventHandler completedCallback)
66 {
67 this.Address = address;
68 this.MillisecondsTimeout = millisecondsTimeout;
69 this.DownloadProgressCallback = downloadProgressCallback;
70 this.CompletedCallback = completedCallback;
71 this.ContentType = contentType;
72 }
73 }
74  
75 internal class ActiveDownload
76 {
77 public List<CapsBase.DownloadProgressEventHandler> ProgresHadlers = new List<CapsBase.DownloadProgressEventHandler>();
78 public List<CapsBase.RequestCompletedEventHandler> CompletedHandlers = new List<CapsBase.RequestCompletedEventHandler>();
79 public HttpWebRequest Request;
80 }
81  
82 /// <summary>
83 /// Manages async HTTP downloads with a limit on maximum
84 /// concurrent downloads
85 /// </summary>
86 public class DownloadManager
87 {
88 Queue<DownloadRequest> queue = new Queue<DownloadRequest>();
89 Dictionary<string, ActiveDownload> activeDownloads = new Dictionary<string, ActiveDownload>();
90  
91 X509Certificate2 m_ClientCert;
92  
93 /// <summary>Maximum number of parallel downloads from a single endpoint</summary>
94 public int ParallelDownloads { get; set; }
95  
96 /// <summary>Client certificate</summary>
97 public X509Certificate2 ClientCert
98 {
99 get { return m_ClientCert; }
100 set { m_ClientCert = value; }
101 }
102  
103 /// <summary>Default constructor</summary>
104 public DownloadManager()
105 {
106 ParallelDownloads = 8;
107 }
108  
109 /// <summary>Cleanup method</summary>
110 public virtual void Dispose()
111 {
112 lock (activeDownloads)
113 {
114 foreach (ActiveDownload download in activeDownloads.Values)
115 {
116 try
117 {
118 if (download.Request != null)
119 {
120 download.Request.Abort();
121 }
122 }
123 catch { }
124 }
125 activeDownloads.Clear();
126 }
127 }
128  
129 /// <summary>Setup http download request</summary>
130 protected virtual HttpWebRequest SetupRequest(Uri address, string acceptHeader)
131 {
132 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(address);
133 request.Method = "GET";
134  
135 if (!string.IsNullOrEmpty(acceptHeader))
136 request.Accept = acceptHeader;
137  
138 // Add the client certificate to the request if one was given
139 if (m_ClientCert != null)
140 request.ClientCertificates.Add(m_ClientCert);
141  
142 // Leave idle connections to this endpoint open for up to 60 seconds
143 request.ServicePoint.MaxIdleTime = 0;
144 // Disable stupid Expect-100: Continue header
145 request.ServicePoint.Expect100Continue = false;
146 // Crank up the max number of connections per endpoint
147 if (request.ServicePoint.ConnectionLimit < Settings.MAX_HTTP_CONNECTIONS)
148 {
149 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);
150 request.ServicePoint.ConnectionLimit = Settings.MAX_HTTP_CONNECTIONS;
151 }
152  
153 return request;
154 }
155  
156 /// <summary>Check the queue for pending work</summary>
157 private void EnqueuePending()
158 {
159 lock (queue)
160 {
161 if (queue.Count > 0)
162 {
163 int nr = 0;
164 lock (activeDownloads)
165 {
166 nr = activeDownloads.Count;
167 }
168  
169 // Logger.DebugLog(nr.ToString() + " active downloads. Queued textures: " + queue.Count.ToString());
170  
171 for (int i = nr; i < ParallelDownloads && queue.Count > 0; i++)
172 {
173 DownloadRequest item = queue.Dequeue();
174 lock (activeDownloads)
175 {
176 string addr = item.Address.ToString();
177 if (activeDownloads.ContainsKey(addr))
178 {
179 activeDownloads[addr].CompletedHandlers.Add(item.CompletedCallback);
180 if (item.DownloadProgressCallback != null)
181 {
182 activeDownloads[addr].ProgresHadlers.Add(item.DownloadProgressCallback);
183 }
184 }
185 else
186 {
187 ActiveDownload activeDownload = new ActiveDownload();
188 activeDownload.CompletedHandlers.Add(item.CompletedCallback);
189 if (item.DownloadProgressCallback != null)
190 {
191 activeDownload.ProgresHadlers.Add(item.DownloadProgressCallback);
192 }
193  
194 Logger.DebugLog("Requesting " + item.Address.ToString());
195 activeDownload.Request = SetupRequest(item.Address, item.ContentType);
196 CapsBase.DownloadDataAsync(
197 activeDownload.Request,
198 item.MillisecondsTimeout,
199 (HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive) =>
200 {
201 foreach (CapsBase.DownloadProgressEventHandler handler in activeDownload.ProgresHadlers)
202 {
203 handler(request, response, bytesReceived, totalBytesToReceive);
204 }
205 },
206 (HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) =>
207 {
208 lock (activeDownloads) activeDownloads.Remove(addr);
209 if (error == null || item.Attempt >= item.Retries || (error != null && error.Message.Contains("404")))
210 {
211 foreach (CapsBase.RequestCompletedEventHandler handler in activeDownload.CompletedHandlers)
212 {
213 handler(request, response, responseData, error);
214 }
215 }
216 else
217 {
218 item.Attempt++;
219 Logger.Log(string.Format("Texture {0} HTTP download failed, trying again retry {1}/{2}",
220 item.Address, item.Attempt, item.Retries), Helpers.LogLevel.Warning);
221 lock (queue) queue.Enqueue(item);
222 }
223  
224 EnqueuePending();
225 }
226 );
227  
228 activeDownloads[addr] = activeDownload;
229 }
230 }
231 }
232 }
233 }
234 }
235  
236 /// <summary>Enqueue a new HTTP download</summary>
237 public void QueueDownload(DownloadRequest req)
238 {
239 lock (activeDownloads)
240 {
241 string addr = req.Address.ToString();
242 if (activeDownloads.ContainsKey(addr))
243 {
244 activeDownloads[addr].CompletedHandlers.Add(req.CompletedCallback);
245 if (req.DownloadProgressCallback != null)
246 {
247 activeDownloads[addr].ProgresHadlers.Add(req.DownloadProgressCallback);
248 }
249 return;
250 }
251 }
252  
253 lock (queue)
254 {
255 queue.Enqueue(req);
256 }
257 EnqueuePending();
258 }
259 }
260 }