diff options
author | John Hurliman | 2010-04-08 12:31:44 -0700 |
---|---|---|
committer | John Hurliman | 2010-04-08 12:31:44 -0700 |
commit | 3f6c4c150e3910e79ee3dc94f9304c16265512c0 (patch) | |
tree | ca83d44c56ab70535dfe04f068f5d0bfd1abb7da | |
parent | * Fixing incorrect documentation for the continuation passing style IAssetSer... (diff) | |
download | opensim-SC-3f6c4c150e3910e79ee3dc94f9304c16265512c0.zip opensim-SC-3f6c4c150e3910e79ee3dc94f9304c16265512c0.tar.gz opensim-SC-3f6c4c150e3910e79ee3dc94f9304c16265512c0.tar.bz2 opensim-SC-3f6c4c150e3910e79ee3dc94f9304c16265512c0.tar.xz |
* Adds IAssetService.GetCached() to allow asset fetching from the local cache only
* Adds GetTextureModule that implements the "GetTexture" capability, aka HTTP texture fetching. This is a significantly optimized path that does not require any server-side JPEG2000 decoding, texture priority queue, or UDP file transfer
* Sanity check for null reference in LLClientView.RefreshGroupMembership()
11 files changed, 293 insertions, 2 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 516c23b..51ab281 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | |||
@@ -11302,9 +11302,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11302 | 11302 | ||
11303 | m_groupPowers.Clear(); | 11303 | m_groupPowers.Clear(); |
11304 | 11304 | ||
11305 | for (int i = 0; i < GroupMembership.Length; i++) | 11305 | if (GroupMembership != null) |
11306 | { | 11306 | { |
11307 | m_groupPowers[GroupMembership[i].GroupID] = GroupMembership[i].GroupPowers; | 11307 | for (int i = 0; i < GroupMembership.Length; i++) |
11308 | { | ||
11309 | m_groupPowers[GroupMembership[i].GroupID] = GroupMembership[i].GroupPowers; | ||
11310 | } | ||
11308 | } | 11311 | } |
11309 | } | 11312 | } |
11310 | } | 11313 | } |
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 37cdaae..9eaa758 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | |||
@@ -406,6 +406,11 @@ namespace Flotsam.RegionModules.AssetCache | |||
406 | return asset; | 406 | return asset; |
407 | } | 407 | } |
408 | 408 | ||
409 | public AssetBase GetCached(string id) | ||
410 | { | ||
411 | return Get(id); | ||
412 | } | ||
413 | |||
409 | public void Expire(string id) | 414 | public void Expire(string id) |
410 | { | 415 | { |
411 | if (m_LogLevel >= 2) | 416 | if (m_LogLevel >= 2) |
diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs new file mode 100644 index 0000000..53d2cef --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
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 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Specialized; | ||
31 | using System.Reflection; | ||
32 | using System.IO; | ||
33 | using System.Web; | ||
34 | using log4net; | ||
35 | using Nini.Config; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.StructuredData; | ||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Framework.Servers; | ||
40 | using OpenSim.Framework.Servers.HttpServer; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | using OpenSim.Services.Interfaces; | ||
44 | using Caps = OpenSim.Framework.Capabilities.Caps; | ||
45 | |||
46 | namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps | ||
47 | { | ||
48 | #region Stream Handler | ||
49 | |||
50 | public delegate byte[] StreamHandlerCallback(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse); | ||
51 | |||
52 | public class StreamHandler : BaseStreamHandler | ||
53 | { | ||
54 | StreamHandlerCallback m_callback; | ||
55 | |||
56 | public StreamHandler(string httpMethod, string path, StreamHandlerCallback callback) | ||
57 | : base(httpMethod, path) | ||
58 | { | ||
59 | m_callback = callback; | ||
60 | } | ||
61 | |||
62 | public override byte[] Handle(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
63 | { | ||
64 | return m_callback(path, request, httpRequest, httpResponse); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | #endregion Stream Handler | ||
69 | |||
70 | public class GetTextureModule : IRegionModule | ||
71 | { | ||
72 | private static readonly ILog m_log = | ||
73 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
74 | private Scene m_scene; | ||
75 | private IAssetService m_assetService; | ||
76 | |||
77 | #region IRegionModule Members | ||
78 | |||
79 | public void Initialise(Scene pScene, IConfigSource pSource) | ||
80 | { | ||
81 | m_scene = pScene; | ||
82 | } | ||
83 | |||
84 | public void PostInitialise() | ||
85 | { | ||
86 | m_assetService = m_scene.RequestModuleInterface<IAssetService>(); | ||
87 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | ||
88 | } | ||
89 | |||
90 | public void Close() { } | ||
91 | |||
92 | public string Name { get { return "GetTextureModule"; } } | ||
93 | public bool IsSharedModule { get { return false; } } | ||
94 | |||
95 | public void RegisterCaps(UUID agentID, Caps caps) | ||
96 | { | ||
97 | UUID capID = UUID.Random(); | ||
98 | |||
99 | m_log.Info("[GETTEXTURE]: /CAPS/" + capID); | ||
100 | caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); | ||
101 | } | ||
102 | |||
103 | #endregion | ||
104 | |||
105 | private byte[] ProcessGetTexture(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
106 | { | ||
107 | // TODO: Change this to a config option | ||
108 | const string REDIRECT_URL = null; | ||
109 | |||
110 | // Try to parse the texture ID from the request URL | ||
111 | NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); | ||
112 | string textureStr = query.GetOne("texture_id"); | ||
113 | |||
114 | if (m_assetService == null) | ||
115 | { | ||
116 | m_log.Error("[GETTEXTURE]: Cannot fetch texture " + textureStr + " without an asset service"); | ||
117 | httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; | ||
118 | return null; | ||
119 | } | ||
120 | |||
121 | UUID textureID; | ||
122 | if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID)) | ||
123 | { | ||
124 | AssetBase texture; | ||
125 | |||
126 | if (!String.IsNullOrEmpty(REDIRECT_URL)) | ||
127 | { | ||
128 | // Only try to fetch locally cached textures. Misses are redirected | ||
129 | texture = m_assetService.GetCached(textureID.ToString()); | ||
130 | |||
131 | if (texture != null) | ||
132 | { | ||
133 | SendTexture(httpRequest, httpResponse, texture); | ||
134 | } | ||
135 | else | ||
136 | { | ||
137 | string textureUrl = REDIRECT_URL + textureID.ToString(); | ||
138 | m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl); | ||
139 | httpResponse.RedirectLocation = textureUrl; | ||
140 | } | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | // Fetch locally or remotely. Misses return a 404 | ||
145 | texture = m_assetService.Get(textureID.ToString()); | ||
146 | |||
147 | if (texture != null) | ||
148 | { | ||
149 | SendTexture(httpRequest, httpResponse, texture); | ||
150 | } | ||
151 | else | ||
152 | { | ||
153 | m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found"); | ||
154 | httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | else | ||
159 | { | ||
160 | m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + httpRequest.Url); | ||
161 | } | ||
162 | |||
163 | httpResponse.Send(); | ||
164 | return null; | ||
165 | } | ||
166 | |||
167 | private void SendTexture(OSHttpRequest request, OSHttpResponse response, AssetBase texture) | ||
168 | { | ||
169 | string range = request.Headers.GetOne("Range"); | ||
170 | if (!String.IsNullOrEmpty(range)) | ||
171 | { | ||
172 | // Range request | ||
173 | int start, end; | ||
174 | if (TryParseRange(range, out start, out end)) | ||
175 | { | ||
176 | end = Utils.Clamp(end, 1, texture.Data.Length); | ||
177 | start = Utils.Clamp(start, 0, end - 1); | ||
178 | |||
179 | m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID); | ||
180 | |||
181 | if (end - start < texture.Data.Length) | ||
182 | response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; | ||
183 | |||
184 | response.ContentLength = end - start; | ||
185 | response.ContentType = texture.Metadata.ContentType; | ||
186 | |||
187 | response.Body.Write(texture.Data, start, end - start); | ||
188 | } | ||
189 | else | ||
190 | { | ||
191 | m_log.Warn("Malformed Range header: " + range); | ||
192 | response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; | ||
193 | } | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | // Full content request | ||
198 | response.ContentLength = texture.Data.Length; | ||
199 | response.ContentType = texture.Metadata.ContentType; | ||
200 | response.Body.Write(texture.Data, 0, texture.Data.Length); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | private bool TryParseRange(string header, out int start, out int end) | ||
205 | { | ||
206 | if (header.StartsWith("bytes=")) | ||
207 | { | ||
208 | string[] rangeValues = header.Substring(6).Split('-'); | ||
209 | if (rangeValues.Length == 2) | ||
210 | { | ||
211 | if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end)) | ||
212 | return true; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | start = end = 0; | ||
217 | return false; | ||
218 | } | ||
219 | } | ||
220 | } | ||
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs index af2f3d6..ebd6bbd 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs | |||
@@ -229,6 +229,14 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset | |||
229 | return asset; | 229 | return asset; |
230 | } | 230 | } |
231 | 231 | ||
232 | public AssetBase GetCached(string id) | ||
233 | { | ||
234 | if (m_Cache != null) | ||
235 | return m_Cache.Get(id); | ||
236 | |||
237 | return null; | ||
238 | } | ||
239 | |||
232 | public AssetMetadata GetMetadata(string id) | 240 | public AssetMetadata GetMetadata(string id) |
233 | { | 241 | { |
234 | AssetBase asset = null; | 242 | AssetBase asset = null; |
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs index 50348da..1b3419d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs | |||
@@ -165,6 +165,14 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset | |||
165 | return asset; | 165 | return asset; |
166 | } | 166 | } |
167 | 167 | ||
168 | public AssetBase GetCached(string id) | ||
169 | { | ||
170 | if (m_Cache != null) | ||
171 | return m_Cache.Get(id); | ||
172 | |||
173 | return null; | ||
174 | } | ||
175 | |||
168 | public AssetMetadata GetMetadata(string id) | 176 | public AssetMetadata GetMetadata(string id) |
169 | { | 177 | { |
170 | AssetBase asset = null; | 178 | AssetBase asset = null; |
diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index b9723a8..ed87f3f 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs | |||
@@ -93,6 +93,11 @@ namespace OpenSim.Services.AssetService | |||
93 | return m_Database.GetAsset(assetID); | 93 | return m_Database.GetAsset(assetID); |
94 | } | 94 | } |
95 | 95 | ||
96 | public AssetBase GetCached(string id) | ||
97 | { | ||
98 | return Get(id); | ||
99 | } | ||
100 | |||
96 | public AssetMetadata GetMetadata(string id) | 101 | public AssetMetadata GetMetadata(string id) |
97 | { | 102 | { |
98 | UUID assetID; | 103 | UUID assetID; |
diff --git a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs index a5c157d..65b3537 100644 --- a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs | |||
@@ -111,6 +111,14 @@ namespace OpenSim.Services.Connectors | |||
111 | return asset; | 111 | return asset; |
112 | } | 112 | } |
113 | 113 | ||
114 | public AssetBase GetCached(string id) | ||
115 | { | ||
116 | if (m_Cache != null) | ||
117 | return m_Cache.Get(id); | ||
118 | |||
119 | return null; | ||
120 | } | ||
121 | |||
114 | public AssetMetadata GetMetadata(string id) | 122 | public AssetMetadata GetMetadata(string id) |
115 | { | 123 | { |
116 | if (m_Cache != null) | 124 | if (m_Cache != null) |
diff --git a/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs index 677169d..34df54a 100644 --- a/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs | |||
@@ -116,6 +116,20 @@ namespace OpenSim.Services.Connectors | |||
116 | return null; | 116 | return null; |
117 | } | 117 | } |
118 | 118 | ||
119 | public AssetBase GetCached(string id) | ||
120 | { | ||
121 | string url = string.Empty; | ||
122 | string assetID = string.Empty; | ||
123 | |||
124 | if (StringToUrlAndAssetID(id, out url, out assetID)) | ||
125 | { | ||
126 | IAssetService connector = GetConnector(url); | ||
127 | return connector.GetCached(assetID); | ||
128 | } | ||
129 | |||
130 | return null; | ||
131 | } | ||
132 | |||
119 | public AssetMetadata GetMetadata(string id) | 133 | public AssetMetadata GetMetadata(string id) |
120 | { | 134 | { |
121 | string url = string.Empty; | 135 | string url = string.Empty; |
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs index 36c51c3..79e49a1 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs | |||
@@ -123,6 +123,14 @@ namespace OpenSim.Services.Connectors.SimianGrid | |||
123 | return GetRemote(id); | 123 | return GetRemote(id); |
124 | } | 124 | } |
125 | 125 | ||
126 | public AssetBase GetCached(string id) | ||
127 | { | ||
128 | if (m_cache != null) | ||
129 | return m_cache.Get(id); | ||
130 | |||
131 | return null; | ||
132 | } | ||
133 | |||
126 | /// <summary> | 134 | /// <summary> |
127 | /// Get an asset's metadata | 135 | /// Get an asset's metadata |
128 | /// </summary> | 136 | /// </summary> |
diff --git a/OpenSim/Services/Interfaces/IAssetService.cs b/OpenSim/Services/Interfaces/IAssetService.cs index f8bedfe..3be6815 100644 --- a/OpenSim/Services/Interfaces/IAssetService.cs +++ b/OpenSim/Services/Interfaces/IAssetService.cs | |||
@@ -51,6 +51,13 @@ namespace OpenSim.Services.Interfaces | |||
51 | byte[] GetData(string id); | 51 | byte[] GetData(string id); |
52 | 52 | ||
53 | /// <summary> | 53 | /// <summary> |
54 | /// Synchronously fetches an asset from the local cache only | ||
55 | /// </summary> | ||
56 | /// <param name="id">Asset ID</param> | ||
57 | /// <returns>The fetched asset, or null if it did not exist in the local cache</returns> | ||
58 | AssetBase GetCached(string id); | ||
59 | |||
60 | /// <summary> | ||
54 | /// Get an asset synchronously or asynchronously (depending on whether | 61 | /// Get an asset synchronously or asynchronously (depending on whether |
55 | /// it is locally cached) and fire a callback with the fetched asset | 62 | /// it is locally cached) and fire a callback with the fetched asset |
56 | /// </summary> | 63 | /// </summary> |
diff --git a/OpenSim/Tests/Common/Mock/MockAssetService.cs b/OpenSim/Tests/Common/Mock/MockAssetService.cs index cb38043..4118308 100644 --- a/OpenSim/Tests/Common/Mock/MockAssetService.cs +++ b/OpenSim/Tests/Common/Mock/MockAssetService.cs | |||
@@ -65,6 +65,11 @@ namespace OpenSim.Tests.Common.Mock | |||
65 | return asset; | 65 | return asset; |
66 | } | 66 | } |
67 | 67 | ||
68 | public AssetBase GetCached(string id) | ||
69 | { | ||
70 | return Get(id); | ||
71 | } | ||
72 | |||
68 | public AssetMetadata GetMetadata(string id) | 73 | public AssetMetadata GetMetadata(string id) |
69 | { | 74 | { |
70 | throw new System.NotImplementedException(); | 75 | throw new System.NotImplementedException(); |