/// <summary>Check the queue for pending work</summary> 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++) { DownloadRequest item = queue.Dequeue(); Logger.DebugLog("Requesting " + item.Address.ToString()); HttpWebRequest req = SetupRequest(item.Address, item.ContentType); CapsBase.DownloadDataAsync( req, item.MillisecondsTimeout, item.DownloadProgressCallback, (HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) => { lock (activeDownloads) activeDownloads.Remove(request); item.CompletedCallback(request, response, responseData, error); EnqueuePending(); } ); lock (activeDownloads) activeDownloads.Add(req); } } } }
/// <summary>Enqueue a new HTPP download</summary> public void QueueDownlad(DownloadRequest req) { lock (queue) { queue.Enqueue(req); } EnqueuePending(); }
/// <summary>Enqueue a new HTPP download</summary> public void QueueDownlad(DownloadRequest req) { 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; } } lock (queue) { queue.Enqueue(req); } EnqueuePending(); }
/// <summary> /// Fetach avatar texture on a grid capable of server side baking /// </summary> /// <param name="avatarID">ID of the avatar</param> /// <param name="textureID">ID of the texture</param> /// <param name="bakeName">Name of the part of the avatar texture applies to</param> /// <param name="callback">Callback invoked on operation completion</param> public void RequestServerBakedImage(UUID avatarID, UUID textureID, string bakeName, TextureDownloadCallback callback) { if (avatarID == UUID.Zero || textureID == UUID.Zero || callback == null) return; if (string.IsNullOrEmpty(Client.Network.AgentAppearanceServiceURL)) { callback(TextureRequestState.NotFound, null); return; } byte[] assetData; // Do we have this image in the cache? if (Client.Assets.Cache.HasAsset(textureID) && (assetData = Client.Assets.Cache.GetCachedAssetBytes(textureID)) != null) { ImageDownload image = new ImageDownload(); image.ID = textureID; image.AssetData = assetData; image.Size = image.AssetData.Length; image.Transferred = image.AssetData.Length; image.ImageType = ImageType.ServerBaked; image.AssetType = AssetType.Texture; image.Success = true; callback(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)); FireImageProgressEvent(image.ID, image.Transferred, image.Size); return; } CapsBase.DownloadProgressEventHandler progressHandler = null; Uri url = new Uri(string.Format("{0}texture/{1}/{2}/{3}", Client.Network.AgentAppearanceServiceURL, avatarID, bakeName, textureID)); DownloadRequest req = new DownloadRequest( url, Client.Settings.CAPS_TIMEOUT, "image/x-j2c", progressHandler, (HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) => { if (error == null && responseData != null) // success { ImageDownload image = new ImageDownload(); image.ID = textureID; image.AssetData = responseData; image.Size = image.AssetData.Length; image.Transferred = image.AssetData.Length; image.ImageType = ImageType.ServerBaked; image.AssetType = AssetType.Texture; image.Success = true; callback(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)); Client.Assets.Cache.SaveAssetToCache(textureID, responseData); } else // download failed { Logger.Log( string.Format("Failed to fetch server bake {0}: {1}", textureID, (error == null) ? "" : error.Message ), Helpers.LogLevel.Warning, Client); callback(TextureRequestState.Timeout, null); } } ); HttpDownloads.QueueDownload(req); }
/// <summary> /// Requests download of a mesh asset /// </summary> /// <param name="meshID">UUID of the mesh asset</param> /// <param name="callback">Callback when the request completes</param> public void RequestMesh(UUID meshID, MeshDownloadCallback callback) { if (meshID == UUID.Zero || callback == null) return; if (Client.Network.CurrentSim.Caps != null && Client.Network.CurrentSim.Caps.CapabilityURI("GetMesh") != null) { // Do we have this mesh asset in the cache? if (Client.Assets.Cache.HasAsset(meshID)) { callback(true, new AssetMesh(meshID, Client.Assets.Cache.GetCachedAssetBytes(meshID))); return; } Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("GetMesh"); DownloadRequest req = new DownloadRequest( new Uri(string.Format("{0}/?mesh_id={1}", url.ToString(), meshID.ToString())), Client.Settings.CAPS_TIMEOUT, null, null, (HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) => { if (error == null && responseData != null) // success { callback(true, new AssetMesh(meshID, responseData)); Client.Assets.Cache.SaveAssetToCache(meshID, responseData); } else // download failed { Logger.Log( string.Format("Failed to fetch mesh asset {0}: {1}", meshID, (error == null) ? "" : error.Message ), Helpers.LogLevel.Warning, Client); } } ); HttpDownloads.QueueDownload(req); } else { Logger.Log("GetMesh capability not available", Helpers.LogLevel.Error, Client); callback(false, null); } }
// Helper method for downloading textures via GetTexture cap // Same signature as the UDP variant since we need all the params to // pass to the UDP TexturePipeline in case we need to fall back to it // (Linden servers currently (1.42) don't support bakes downloads via HTTP) private void HttpRequestTexture(UUID textureID, ImageType imageType, float priority, int discardLevel, uint packetStart, TextureDownloadCallback callback, bool progress) { if (textureID == UUID.Zero || callback == null) return; byte[] assetData; // Do we have this image in the cache? if (Client.Assets.Cache.HasAsset(textureID) && (assetData = Client.Assets.Cache.GetCachedAssetBytes(textureID)) != null) { ImageDownload image = new ImageDownload(); image.ID = textureID; image.AssetData = assetData; image.Size = image.AssetData.Length; image.Transferred = image.AssetData.Length; image.ImageType = imageType; image.AssetType = AssetType.Texture; image.Success = true; callback(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)); FireImageProgressEvent(image.ID, image.Transferred, image.Size); return; } CapsBase.DownloadProgressEventHandler progressHandler = null; if (progress) { progressHandler = (HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive) => { FireImageProgressEvent(textureID, bytesReceived, totalBytesToReceive); }; } Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("GetTexture"); DownloadRequest req = new DownloadRequest( new Uri(string.Format("{0}/?texture_id={1}", url.ToString(), textureID.ToString())), Client.Settings.CAPS_TIMEOUT, "image/x-j2c", progressHandler, (HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error) => { if (error == null && responseData != null) // success { ImageDownload image = new ImageDownload(); image.ID = textureID; image.AssetData = responseData; image.Size = image.AssetData.Length; image.Transferred = image.AssetData.Length; image.ImageType = imageType; image.AssetType = AssetType.Texture; image.Success = true; callback(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)); FireImageProgressEvent(image.ID, image.Transferred, image.Size); Client.Assets.Cache.SaveAssetToCache(textureID, responseData); } else // download failed { Logger.Log( string.Format("Failed to fetch texture {0} over HTTP, falling back to UDP: {1}", textureID, (error == null) ? "" : error.Message ), Helpers.LogLevel.Warning, Client); Texture.RequestTexture(textureID, imageType, priority, discardLevel, packetStart, callback, progress); } } ); HttpDownloads.QueueDownload(req); }
/// <summary>Check the queue for pending work</summary> private void EnqueuePending() { lock (queue) { if (queue.Count > 0) { int nr = 0; lock (activeDownloads) { nr = activeDownloads.Count; } Logger.DebugLog(nr.ToString() + " active downloads. Queued textures: " + queue.Count.ToString()); for (int i = nr; i < ParallelDownloads && queue.Count > 0; i++) { DownloadRequest item = queue.Dequeue(); lock (activeDownloads) { string addr = item.Address.ToString(); if (activeDownloads.ContainsKey(addr)) { activeDownloads[addr].CompletedHandlers.Add(item.CompletedCallback); if (item.DownloadProgressCallback != null) { activeDownloads[addr].ProgresHadlers.Add(item.DownloadProgressCallback); } } 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); 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 { 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); } EnqueuePending(); } ); activeDownloads[addr] = activeDownload; } } } } } }
/// <summary>Enqueue a new HTTP download</summary> public void QueueDownload(DownloadRequest req) { 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; } } lock (queue) { queue.Enqueue(req); } EnqueuePending(); }
/// <summary>Check the queue for pending work</summary> 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++) { DownloadRequest item = queue.Dequeue(); lock (activeDownloads) { string addr = item.Address.ToString(); if (activeDownloads.ContainsKey(addr)) { activeDownloads[addr].CompletedHandlers.Add(item.CompletedCallback); if (item.DownloadProgressCallback != null) { activeDownloads[addr].ProgresHadlers.Add(item.DownloadProgressCallback); } } 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); foreach (CapsBase.RequestCompletedEventHandler handler in activeDownload.CompletedHandlers) { handler(request, response, responseData, error); } EnqueuePending(); } ); activeDownloads[addr] = activeDownload; } } } } } }