diff options
Diffstat (limited to 'OpenSim/Framework/WebUtil.cs')
-rw-r--r-- | OpenSim/Framework/WebUtil.cs | 240 |
1 files changed, 213 insertions, 27 deletions
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 1c856af..d88d095 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs | |||
@@ -50,6 +50,16 @@ namespace OpenSim.Framework | |||
50 | LogManager.GetLogger( | 50 | LogManager.GetLogger( |
51 | MethodBase.GetCurrentMethod().DeclaringType); | 51 | MethodBase.GetCurrentMethod().DeclaringType); |
52 | 52 | ||
53 | private static int m_requestNumber = 0; | ||
54 | |||
55 | // this is the header field used to communicate the local request id | ||
56 | // used for performance and debugging | ||
57 | public const string OSHeaderRequestID = "opensim-request-id"; | ||
58 | |||
59 | // number of milliseconds a call can take before it is considered | ||
60 | // a "long" call for warning & debugging purposes | ||
61 | public const int LongCallTime = 200; | ||
62 | |||
53 | /// <summary> | 63 | /// <summary> |
54 | /// Send LLSD to an HTTP client in application/llsd+json form | 64 | /// Send LLSD to an HTTP client in application/llsd+json form |
55 | /// </summary> | 65 | /// </summary> |
@@ -123,26 +133,186 @@ namespace OpenSim.Framework | |||
123 | } | 133 | } |
124 | 134 | ||
125 | /// <summary> | 135 | /// <summary> |
136 | /// PUT JSON-encoded data to a web service that returns LLSD or | ||
137 | /// JSON data | ||
138 | /// </summary> | ||
139 | public static OSDMap PutToService(string url, OSDMap data) | ||
140 | { | ||
141 | return ServiceOSDRequest(url,data,"PUT",10000); | ||
142 | } | ||
143 | |||
144 | public static OSDMap PostToService(string url, OSDMap data) | ||
145 | { | ||
146 | return ServiceOSDRequest(url,data,"POST",10000); | ||
147 | } | ||
148 | |||
149 | public static OSDMap GetFromService(string url) | ||
150 | { | ||
151 | return ServiceOSDRequest(url,null,"GET",10000); | ||
152 | } | ||
153 | |||
154 | public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout) | ||
155 | { | ||
156 | int reqnum = m_requestNumber++; | ||
157 | // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method); | ||
158 | |||
159 | string errorMessage = "unknown error"; | ||
160 | int tickstart = Util.EnvironmentTickCount(); | ||
161 | int tickdata = 0; | ||
162 | |||
163 | try | ||
164 | { | ||
165 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); | ||
166 | request.Method = method; | ||
167 | request.Timeout = timeout; | ||
168 | request.KeepAlive = false; | ||
169 | request.MaximumAutomaticRedirections = 10; | ||
170 | request.ReadWriteTimeout = timeout / 4; | ||
171 | request.Headers[OSHeaderRequestID] = reqnum.ToString(); | ||
172 | |||
173 | // If there is some input, write it into the request | ||
174 | if (data != null) | ||
175 | { | ||
176 | string strBuffer = OSDParser.SerializeJsonString(data); | ||
177 | byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer); | ||
178 | |||
179 | request.ContentType = "application/json"; | ||
180 | request.ContentLength = buffer.Length; //Count bytes to send | ||
181 | using (Stream requestStream = request.GetRequestStream()) | ||
182 | requestStream.Write(buffer, 0, buffer.Length); //Send it | ||
183 | } | ||
184 | |||
185 | // capture how much time was spent writing, this may seem silly | ||
186 | // but with the number concurrent requests, this often blocks | ||
187 | tickdata = Util.EnvironmentTickCountSubtract(tickstart); | ||
188 | |||
189 | using (WebResponse response = request.GetResponse()) | ||
190 | { | ||
191 | using (Stream responseStream = response.GetResponseStream()) | ||
192 | { | ||
193 | string responseStr = null; | ||
194 | responseStr = responseStream.GetStreamString(); | ||
195 | // m_log.DebugFormat("[WEB UTIL]: <{0}> response is <{1}>",reqnum,responseStr); | ||
196 | return CanonicalizeResults(responseStr); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | catch (WebException we) | ||
201 | { | ||
202 | errorMessage = we.Message; | ||
203 | if (we.Status == WebExceptionStatus.ProtocolError) | ||
204 | { | ||
205 | HttpWebResponse webResponse = (HttpWebResponse)we.Response; | ||
206 | errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); | ||
207 | } | ||
208 | } | ||
209 | catch (Exception ex) | ||
210 | { | ||
211 | errorMessage = ex.Message; | ||
212 | } | ||
213 | finally | ||
214 | { | ||
215 | // This just dumps a warning for any operation that takes more than 100 ms | ||
216 | int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); | ||
217 | if (tickdiff > LongCallTime) | ||
218 | m_log.InfoFormat("[WEB UTIL]: osd request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", | ||
219 | reqnum,url,method,tickdiff,tickdata); | ||
220 | } | ||
221 | |||
222 | m_log.WarnFormat("[WEB UTIL] <{0}> osd request failed: {1}",reqnum,errorMessage); | ||
223 | return ErrorResponseMap(errorMessage); | ||
224 | } | ||
225 | |||
226 | /// <summary> | ||
227 | /// Since there are no consistencies in the way web requests are | ||
228 | /// formed, we need to do a little guessing about the result format. | ||
229 | /// Keys: | ||
230 | /// Success|success == the success fail of the request | ||
231 | /// _RawResult == the raw string that came back | ||
232 | /// _Result == the OSD unpacked string | ||
233 | /// </summary> | ||
234 | private static OSDMap CanonicalizeResults(string response) | ||
235 | { | ||
236 | OSDMap result = new OSDMap(); | ||
237 | |||
238 | // Default values | ||
239 | result["Success"] = OSD.FromBoolean(true); | ||
240 | result["success"] = OSD.FromBoolean(true); | ||
241 | result["_RawResult"] = OSD.FromString(response); | ||
242 | result["_Result"] = new OSDMap(); | ||
243 | |||
244 | if (response.Equals("true",System.StringComparison.OrdinalIgnoreCase)) | ||
245 | return result; | ||
246 | |||
247 | if (response.Equals("false",System.StringComparison.OrdinalIgnoreCase)) | ||
248 | { | ||
249 | result["Success"] = OSD.FromBoolean(false); | ||
250 | result["success"] = OSD.FromBoolean(false); | ||
251 | return result; | ||
252 | } | ||
253 | |||
254 | try | ||
255 | { | ||
256 | OSD responseOSD = OSDParser.Deserialize(response); | ||
257 | if (responseOSD.Type == OSDType.Map) | ||
258 | { | ||
259 | result["_Result"] = (OSDMap)responseOSD; | ||
260 | return result; | ||
261 | } | ||
262 | } | ||
263 | catch (Exception e) | ||
264 | { | ||
265 | // don't need to treat this as an error... we're just guessing anyway | ||
266 | m_log.DebugFormat("[WEB UTIL] couldn't decode <{0}>: {1}",response,e.Message); | ||
267 | } | ||
268 | |||
269 | return result; | ||
270 | } | ||
271 | |||
272 | /// <summary> | ||
126 | /// POST URL-encoded form data to a web service that returns LLSD or | 273 | /// POST URL-encoded form data to a web service that returns LLSD or |
127 | /// JSON data | 274 | /// JSON data |
128 | /// </summary> | 275 | /// </summary> |
129 | public static OSDMap PostToService(string url, NameValueCollection data) | 276 | public static OSDMap PostToService(string url, NameValueCollection data) |
130 | { | 277 | { |
131 | string errorMessage; | 278 | return ServiceFormRequest(url,data,10000); |
279 | } | ||
280 | |||
281 | public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout) | ||
282 | { | ||
283 | int reqnum = m_requestNumber++; | ||
284 | string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown"; | ||
285 | // m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method); | ||
286 | |||
287 | string errorMessage = "unknown error"; | ||
288 | int tickstart = Util.EnvironmentTickCount(); | ||
289 | int tickdata = 0; | ||
132 | 290 | ||
133 | try | 291 | try |
134 | { | 292 | { |
135 | string queryString = BuildQueryString(data); | 293 | |
136 | byte[] requestData = System.Text.Encoding.UTF8.GetBytes(queryString); | ||
137 | |||
138 | HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); | 294 | HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); |
139 | request.Method = "POST"; | 295 | request.Method = "POST"; |
140 | request.ContentLength = requestData.Length; | 296 | request.Timeout = timeout; |
141 | request.ContentType = "application/x-www-form-urlencoded"; | 297 | request.KeepAlive = false; |
298 | request.MaximumAutomaticRedirections = 10; | ||
299 | request.ReadWriteTimeout = timeout / 4; | ||
300 | request.Headers[OSHeaderRequestID] = reqnum.ToString(); | ||
301 | |||
302 | if (data != null) | ||
303 | { | ||
304 | string queryString = BuildQueryString(data); | ||
305 | byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString); | ||
306 | |||
307 | request.ContentLength = buffer.Length; | ||
308 | request.ContentType = "application/x-www-form-urlencoded"; | ||
309 | using (Stream requestStream = request.GetRequestStream()) | ||
310 | requestStream.Write(buffer, 0, buffer.Length); | ||
311 | } | ||
142 | 312 | ||
143 | Stream requestStream = request.GetRequestStream(); | 313 | // capture how much time was spent writing, this may seem silly |
144 | requestStream.Write(requestData, 0, requestData.Length); | 314 | // but with the number concurrent requests, this often blocks |
145 | requestStream.Close(); | 315 | tickdata = Util.EnvironmentTickCountSubtract(tickstart); |
146 | 316 | ||
147 | using (WebResponse response = request.GetResponse()) | 317 | using (WebResponse response = request.GetResponse()) |
148 | { | 318 | { |
@@ -150,34 +320,50 @@ namespace OpenSim.Framework | |||
150 | { | 320 | { |
151 | string responseStr = null; | 321 | string responseStr = null; |
152 | 322 | ||
153 | try | 323 | responseStr = responseStream.GetStreamString(); |
154 | { | 324 | OSD responseOSD = OSDParser.Deserialize(responseStr); |
155 | responseStr = responseStream.GetStreamString(); | 325 | if (responseOSD.Type == OSDType.Map) |
156 | OSD responseOSD = OSDParser.Deserialize(responseStr); | 326 | return (OSDMap)responseOSD; |
157 | if (responseOSD.Type == OSDType.Map) | ||
158 | return (OSDMap)responseOSD; | ||
159 | else | ||
160 | errorMessage = "Response format was invalid."; | ||
161 | } | ||
162 | catch (Exception ex) | ||
163 | { | ||
164 | if (!String.IsNullOrEmpty(responseStr)) | ||
165 | errorMessage = "Failed to parse the response:\n" + responseStr; | ||
166 | else | ||
167 | errorMessage = "Failed to retrieve the response: " + ex.Message; | ||
168 | } | ||
169 | } | 327 | } |
170 | } | 328 | } |
171 | } | 329 | } |
330 | catch (WebException we) | ||
331 | { | ||
332 | errorMessage = we.Message; | ||
333 | if (we.Status == WebExceptionStatus.ProtocolError) | ||
334 | { | ||
335 | HttpWebResponse webResponse = (HttpWebResponse)we.Response; | ||
336 | errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); | ||
337 | } | ||
338 | } | ||
172 | catch (Exception ex) | 339 | catch (Exception ex) |
173 | { | 340 | { |
174 | m_log.Warn("POST to URL " + url + " failed: " + ex); | ||
175 | errorMessage = ex.Message; | 341 | errorMessage = ex.Message; |
176 | } | 342 | } |
343 | finally | ||
344 | { | ||
345 | int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); | ||
346 | if (tickdiff > LongCallTime) | ||
347 | m_log.InfoFormat("[WEB UTIL]: form request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", | ||
348 | reqnum,url,method,tickdiff,tickdata); | ||
349 | } | ||
177 | 350 | ||
178 | return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; | 351 | m_log.WarnFormat("[WEB UTIL]: <{0}> form request failed: {1}",reqnum,errorMessage); |
352 | return ErrorResponseMap(errorMessage); | ||
179 | } | 353 | } |
180 | 354 | ||
355 | /// <summary> | ||
356 | /// Create a response map for an error, trying to keep | ||
357 | /// the result formats consistent | ||
358 | /// </summary> | ||
359 | private static OSDMap ErrorResponseMap(string msg) | ||
360 | { | ||
361 | OSDMap result = new OSDMap(); | ||
362 | result["Success"] = "False"; | ||
363 | result["Message"] = OSD.FromString("Service request failed: " + msg); | ||
364 | return result; | ||
365 | } | ||
366 | |||
181 | #region Uri | 367 | #region Uri |
182 | 368 | ||
183 | /// <summary> | 369 | /// <summary> |