corrade-vassal – Blame information for rev 1
?pathlinks?
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 | } |