diff options
author | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
commit | 134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch) | |
tree | 216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/Scripting/HttpRequest | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz |
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/HttpRequest')
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs | 355 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs | 199 |
2 files changed, 467 insertions, 87 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index a676971..9dfeb96 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs | |||
@@ -28,12 +28,15 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.IO; | 30 | using System.IO; |
31 | using System.Linq; | ||
31 | using System.Net; | 32 | using System.Net; |
32 | using System.Net.Mail; | 33 | using System.Net.Mail; |
33 | using System.Net.Security; | 34 | using System.Net.Security; |
35 | using System.Reflection; | ||
34 | using System.Text; | 36 | using System.Text; |
35 | using System.Threading; | 37 | using System.Threading; |
36 | using System.Security.Cryptography.X509Certificates; | 38 | using System.Security.Cryptography.X509Certificates; |
39 | using log4net; | ||
37 | using Nini.Config; | 40 | using Nini.Config; |
38 | using OpenMetaverse; | 41 | using OpenMetaverse; |
39 | using OpenSim.Framework; | 42 | using OpenSim.Framework; |
@@ -91,10 +94,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
91 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HttpRequestModule")] | 94 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HttpRequestModule")] |
92 | public class HttpRequestModule : ISharedRegionModule, IHttpRequestModule | 95 | public class HttpRequestModule : ISharedRegionModule, IHttpRequestModule |
93 | { | 96 | { |
97 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
98 | |||
94 | private object HttpListLock = new object(); | 99 | private object HttpListLock = new object(); |
95 | private int httpTimeout = 30000; | 100 | private int httpTimeout = 30000; |
96 | private string m_name = "HttpScriptRequests"; | 101 | private string m_name = "HttpScriptRequests"; |
97 | 102 | ||
103 | private OutboundUrlFilter m_outboundUrlFilter; | ||
98 | private string m_proxyurl = ""; | 104 | private string m_proxyurl = ""; |
99 | private string m_proxyexcepts = ""; | 105 | private string m_proxyexcepts = ""; |
100 | 106 | ||
@@ -132,10 +138,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
132 | return false; | 138 | return false; |
133 | 139 | ||
134 | // Check for policy and execute it if defined | 140 | // Check for policy and execute it if defined |
141 | #pragma warning disable 0618 | ||
135 | if (ServicePointManager.CertificatePolicy != null) | 142 | if (ServicePointManager.CertificatePolicy != null) |
136 | { | 143 | { |
137 | return ServicePointManager.CertificatePolicy.CheckValidationResult (sp, certificate, Request, 0); | 144 | return ServicePointManager.CertificatePolicy.CheckValidationResult (sp, certificate, Request, 0); |
138 | } | 145 | } |
146 | #pragma warning restore 0618 | ||
139 | 147 | ||
140 | return true; | 148 | return true; |
141 | } | 149 | } |
@@ -153,7 +161,9 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
153 | return UUID.Zero; | 161 | return UUID.Zero; |
154 | } | 162 | } |
155 | 163 | ||
156 | public UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body) | 164 | public UUID StartHttpRequest( |
165 | uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body, | ||
166 | out HttpInitialRequestStatus status) | ||
157 | { | 167 | { |
158 | UUID reqID = UUID.Random(); | 168 | UUID reqID = UUID.Random(); |
159 | HttpRequestClass htc = new HttpRequestClass(); | 169 | HttpRequestClass htc = new HttpRequestClass(); |
@@ -187,10 +197,50 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
187 | case (int)HttpRequestConstants.HTTP_VERIFY_CERT: | 197 | case (int)HttpRequestConstants.HTTP_VERIFY_CERT: |
188 | htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0); | 198 | htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0); |
189 | break; | 199 | break; |
200 | |||
201 | case (int)HttpRequestConstants.HTTP_VERBOSE_THROTTLE: | ||
202 | |||
203 | // TODO implement me | ||
204 | break; | ||
205 | |||
206 | case (int)HttpRequestConstants.HTTP_CUSTOM_HEADER: | ||
207 | //Parameters are in pairs and custom header takes | ||
208 | //arguments in pairs so adjust for header marker. | ||
209 | ++i; | ||
210 | |||
211 | //Maximum of 8 headers are allowed based on the | ||
212 | //Second Life documentation for llHTTPRequest. | ||
213 | for (int count = 1; count <= 8; ++count) | ||
214 | { | ||
215 | //Not enough parameters remaining for a header? | ||
216 | if (parms.Length - i < 2) | ||
217 | break; | ||
218 | |||
219 | //Have we reached the end of the list of headers? | ||
220 | //End is marked by a string with a single digit. | ||
221 | //We already know we have at least one parameter | ||
222 | //so it is safe to do this check at top of loop. | ||
223 | if (Char.IsDigit(parms[i][0])) | ||
224 | break; | ||
225 | |||
226 | if (htc.HttpCustomHeaders == null) | ||
227 | htc.HttpCustomHeaders = new List<string>(); | ||
228 | |||
229 | htc.HttpCustomHeaders.Add(parms[i]); | ||
230 | htc.HttpCustomHeaders.Add(parms[i+1]); | ||
231 | |||
232 | i += 2; | ||
233 | } | ||
234 | break; | ||
235 | |||
236 | case (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE: | ||
237 | htc.HttpPragmaNoCache = (int.Parse(parms[i + 1]) != 0); | ||
238 | break; | ||
190 | } | 239 | } |
191 | } | 240 | } |
192 | } | 241 | } |
193 | 242 | ||
243 | htc.RequestModule = this; | ||
194 | htc.LocalID = localID; | 244 | htc.LocalID = localID; |
195 | htc.ItemID = itemID; | 245 | htc.ItemID = itemID; |
196 | htc.Url = url; | 246 | htc.Url = url; |
@@ -201,28 +251,68 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
201 | htc.proxyurl = m_proxyurl; | 251 | htc.proxyurl = m_proxyurl; |
202 | htc.proxyexcepts = m_proxyexcepts; | 252 | htc.proxyexcepts = m_proxyexcepts; |
203 | 253 | ||
254 | // Same number as default HttpWebRequest.MaximumAutomaticRedirections | ||
255 | htc.MaxRedirects = 50; | ||
256 | |||
257 | if (StartHttpRequest(htc)) | ||
258 | { | ||
259 | status = HttpInitialRequestStatus.OK; | ||
260 | return htc.ReqID; | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER; | ||
265 | return UUID.Zero; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | /// <summary> | ||
270 | /// Would a caller to this module be allowed to make a request to the given URL? | ||
271 | /// </summary> | ||
272 | /// <returns></returns> | ||
273 | public bool CheckAllowed(Uri url) | ||
274 | { | ||
275 | return m_outboundUrlFilter.CheckAllowed(url); | ||
276 | } | ||
277 | |||
278 | public bool StartHttpRequest(HttpRequestClass req) | ||
279 | { | ||
280 | if (!CheckAllowed(new Uri(req.Url))) | ||
281 | return false; | ||
282 | |||
204 | lock (HttpListLock) | 283 | lock (HttpListLock) |
205 | { | 284 | { |
206 | m_pendingRequests.Add(reqID, htc); | 285 | m_pendingRequests.Add(req.ReqID, req); |
207 | } | 286 | } |
208 | 287 | ||
209 | htc.Process(); | 288 | req.Process(); |
210 | 289 | ||
211 | return reqID; | 290 | return true; |
212 | } | 291 | } |
213 | 292 | ||
214 | public void StopHttpRequest(uint m_localID, UUID m_itemID) | 293 | public void StopHttpRequestsForScript(UUID id) |
215 | { | 294 | { |
216 | if (m_pendingRequests != null) | 295 | if (m_pendingRequests != null) |
217 | { | 296 | { |
297 | List<UUID> keysToRemove = null; | ||
298 | |||
218 | lock (HttpListLock) | 299 | lock (HttpListLock) |
219 | { | 300 | { |
220 | HttpRequestClass tmpReq; | 301 | foreach (HttpRequestClass req in m_pendingRequests.Values) |
221 | if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq)) | ||
222 | { | 302 | { |
223 | tmpReq.Stop(); | 303 | if (req.ItemID == id) |
224 | m_pendingRequests.Remove(m_itemID); | 304 | { |
305 | req.Stop(); | ||
306 | |||
307 | if (keysToRemove == null) | ||
308 | keysToRemove = new List<UUID>(); | ||
309 | |||
310 | keysToRemove.Add(req.ReqID); | ||
311 | } | ||
225 | } | 312 | } |
313 | |||
314 | if (keysToRemove != null) | ||
315 | keysToRemove.ForEach(keyToRemove => m_pendingRequests.Remove(keyToRemove)); | ||
226 | } | 316 | } |
227 | } | 317 | } |
228 | } | 318 | } |
@@ -240,19 +330,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
240 | { | 330 | { |
241 | lock (HttpListLock) | 331 | lock (HttpListLock) |
242 | { | 332 | { |
243 | foreach (UUID luid in m_pendingRequests.Keys) | 333 | foreach (HttpRequestClass req in m_pendingRequests.Values) |
244 | { | 334 | { |
245 | HttpRequestClass tmpReq; | 335 | if (req.Finished) |
246 | 336 | return req; | |
247 | if (m_pendingRequests.TryGetValue(luid, out tmpReq)) | ||
248 | { | ||
249 | if (tmpReq.Finished) | ||
250 | { | ||
251 | return tmpReq; | ||
252 | } | ||
253 | } | ||
254 | } | 337 | } |
255 | } | 338 | } |
339 | |||
256 | return null; | 340 | return null; |
257 | } | 341 | } |
258 | 342 | ||
@@ -279,6 +363,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
279 | m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); | 363 | m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); |
280 | m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); | 364 | m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); |
281 | 365 | ||
366 | m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config); | ||
367 | |||
282 | m_pendingRequests = new Dictionary<UUID, HttpRequestClass>(); | 368 | m_pendingRequests = new Dictionary<UUID, HttpRequestClass>(); |
283 | } | 369 | } |
284 | 370 | ||
@@ -321,16 +407,27 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
321 | #endregion | 407 | #endregion |
322 | } | 408 | } |
323 | 409 | ||
324 | public class HttpRequestClass: IServiceRequest | 410 | public class HttpRequestClass : IServiceRequest |
325 | { | 411 | { |
412 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
413 | |||
326 | // Constants for parameters | 414 | // Constants for parameters |
327 | // public const int HTTP_BODY_MAXLENGTH = 2; | 415 | // public const int HTTP_BODY_MAXLENGTH = 2; |
328 | // public const int HTTP_METHOD = 0; | 416 | // public const int HTTP_METHOD = 0; |
329 | // public const int HTTP_MIMETYPE = 1; | 417 | // public const int HTTP_MIMETYPE = 1; |
330 | // public const int HTTP_VERIFY_CERT = 3; | 418 | // public const int HTTP_VERIFY_CERT = 3; |
419 | // public const int HTTP_VERBOSE_THROTTLE = 4; | ||
420 | // public const int HTTP_CUSTOM_HEADER = 5; | ||
421 | // public const int HTTP_PRAGMA_NO_CACHE = 6; | ||
422 | |||
423 | /// <summary> | ||
424 | /// Module that made this request. | ||
425 | /// </summary> | ||
426 | public HttpRequestModule RequestModule { get; set; } | ||
427 | |||
331 | private bool _finished; | 428 | private bool _finished; |
332 | public bool Finished | 429 | public bool Finished |
333 | { | 430 | { |
334 | get { return _finished; } | 431 | get { return _finished; } |
335 | } | 432 | } |
336 | // public int HttpBodyMaxLen = 2048; // not implemented | 433 | // public int HttpBodyMaxLen = 2048; // not implemented |
@@ -340,11 +437,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
340 | public string HttpMIMEType = "text/plain;charset=utf-8"; | 437 | public string HttpMIMEType = "text/plain;charset=utf-8"; |
341 | public int HttpTimeout; | 438 | public int HttpTimeout; |
342 | public bool HttpVerifyCert = true; | 439 | public bool HttpVerifyCert = true; |
343 | private Thread httpThread; | 440 | //public bool HttpVerboseThrottle = true; // not implemented |
441 | public List<string> HttpCustomHeaders = null; | ||
442 | public bool HttpPragmaNoCache = true; | ||
344 | 443 | ||
345 | // Request info | 444 | // Request info |
346 | private UUID _itemID; | 445 | private UUID _itemID; |
347 | public UUID ItemID | 446 | public UUID ItemID |
348 | { | 447 | { |
349 | get { return _itemID; } | 448 | get { return _itemID; } |
350 | set { _itemID = value; } | 449 | set { _itemID = value; } |
@@ -358,9 +457,20 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
358 | public DateTime Next; | 457 | public DateTime Next; |
359 | public string proxyurl; | 458 | public string proxyurl; |
360 | public string proxyexcepts; | 459 | public string proxyexcepts; |
460 | |||
461 | /// <summary> | ||
462 | /// Number of HTTP redirects that this request has been through. | ||
463 | /// </summary> | ||
464 | public int Redirects { get; private set; } | ||
465 | |||
466 | /// <summary> | ||
467 | /// Maximum number of HTTP redirects allowed for this request. | ||
468 | /// </summary> | ||
469 | public int MaxRedirects { get; set; } | ||
470 | |||
361 | public string OutboundBody; | 471 | public string OutboundBody; |
362 | private UUID _reqID; | 472 | private UUID _reqID; |
363 | public UUID ReqID | 473 | public UUID ReqID |
364 | { | 474 | { |
365 | get { return _reqID; } | 475 | get { return _reqID; } |
366 | set { _reqID = value; } | 476 | set { _reqID = value; } |
@@ -374,34 +484,19 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
374 | 484 | ||
375 | public void Process() | 485 | public void Process() |
376 | { | 486 | { |
377 | httpThread = new Thread(SendRequest); | 487 | SendRequest(); |
378 | httpThread.Name = "HttpRequestThread"; | ||
379 | httpThread.Priority = ThreadPriority.BelowNormal; | ||
380 | httpThread.IsBackground = true; | ||
381 | _finished = false; | ||
382 | httpThread.Start(); | ||
383 | } | 488 | } |
384 | 489 | ||
385 | /* | ||
386 | * TODO: More work on the response codes. Right now | ||
387 | * returning 200 for success or 499 for exception | ||
388 | */ | ||
389 | |||
390 | public void SendRequest() | 490 | public void SendRequest() |
391 | { | 491 | { |
392 | HttpWebResponse response = null; | ||
393 | StringBuilder sb = new StringBuilder(); | ||
394 | byte[] buf = new byte[8192]; | ||
395 | string tempString = null; | ||
396 | int count = 0; | ||
397 | |||
398 | try | 492 | try |
399 | { | 493 | { |
400 | Request = (HttpWebRequest) WebRequest.Create(Url); | 494 | Request = (HttpWebRequest)WebRequest.Create(Url); |
495 | Request.AllowAutoRedirect = false; | ||
401 | Request.Method = HttpMethod; | 496 | Request.Method = HttpMethod; |
402 | Request.ContentType = HttpMIMEType; | 497 | Request.ContentType = HttpMIMEType; |
403 | 498 | ||
404 | if(!HttpVerifyCert) | 499 | if (!HttpVerifyCert) |
405 | { | 500 | { |
406 | // We could hijack Connection Group Name to identify | 501 | // We could hijack Connection Group Name to identify |
407 | // a desired security exception. But at the moment we'll use a dummy header instead. | 502 | // a desired security exception. But at the moment we'll use a dummy header instead. |
@@ -412,41 +507,57 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
412 | // { | 507 | // { |
413 | // Request.ConnectionGroupName="Verify"; | 508 | // Request.ConnectionGroupName="Verify"; |
414 | // } | 509 | // } |
415 | if (proxyurl != null && proxyurl.Length > 0) | 510 | |
511 | if (!HttpPragmaNoCache) | ||
512 | { | ||
513 | Request.Headers.Add("Pragma", "no-cache"); | ||
514 | } | ||
515 | |||
516 | if (HttpCustomHeaders != null) | ||
517 | { | ||
518 | for (int i = 0; i < HttpCustomHeaders.Count; i += 2) | ||
519 | Request.Headers.Add(HttpCustomHeaders[i], | ||
520 | HttpCustomHeaders[i+1]); | ||
521 | } | ||
522 | |||
523 | if (!string.IsNullOrEmpty(proxyurl)) | ||
416 | { | 524 | { |
417 | if (proxyexcepts != null && proxyexcepts.Length > 0) | 525 | if (!string.IsNullOrEmpty(proxyexcepts)) |
418 | { | 526 | { |
419 | string[] elist = proxyexcepts.Split(';'); | 527 | string[] elist = proxyexcepts.Split(';'); |
420 | Request.Proxy = new WebProxy(proxyurl, true, elist); | 528 | Request.Proxy = new WebProxy(proxyurl, true, elist); |
421 | } | 529 | } |
422 | else | 530 | else |
423 | { | 531 | { |
424 | Request.Proxy = new WebProxy(proxyurl, true); | 532 | Request.Proxy = new WebProxy(proxyurl, true); |
425 | } | 533 | } |
426 | } | 534 | } |
427 | 535 | ||
428 | foreach (KeyValuePair<string, string> entry in ResponseHeaders) | 536 | if (ResponseHeaders != null) |
429 | if (entry.Key.ToLower().Equals("user-agent")) | 537 | { |
430 | Request.UserAgent = entry.Value; | 538 | foreach (KeyValuePair<string, string> entry in ResponseHeaders) |
431 | else | 539 | if (entry.Key.ToLower().Equals("user-agent") && Request is HttpWebRequest) |
432 | Request.Headers[entry.Key] = entry.Value; | 540 | ((HttpWebRequest)Request).UserAgent = entry.Value; |
541 | else | ||
542 | Request.Headers[entry.Key] = entry.Value; | ||
543 | } | ||
433 | 544 | ||
434 | // Encode outbound data | 545 | // Encode outbound data |
435 | if (OutboundBody.Length > 0) | 546 | if (!string.IsNullOrEmpty(OutboundBody)) |
436 | { | 547 | { |
437 | byte[] data = Util.UTF8.GetBytes(OutboundBody); | 548 | byte[] data = Util.UTF8.GetBytes(OutboundBody); |
438 | 549 | ||
439 | Request.ContentLength = data.Length; | 550 | Request.ContentLength = data.Length; |
440 | Stream bstream = Request.GetRequestStream(); | 551 | using (Stream bstream = Request.GetRequestStream()) |
441 | bstream.Write(data, 0, data.Length); | 552 | bstream.Write(data, 0, data.Length); |
442 | bstream.Close(); | ||
443 | } | 553 | } |
444 | 554 | ||
445 | Request.Timeout = HttpTimeout; | ||
446 | try | 555 | try |
447 | { | 556 | { |
448 | // execute the request | 557 | IAsyncResult result = (IAsyncResult)Request.BeginGetResponse(ResponseCallback, null); |
449 | response = (HttpWebResponse) Request.GetResponse(); | 558 | |
559 | ThreadPool.RegisterWaitForSingleObject( | ||
560 | result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), null, HttpTimeout, true); | ||
450 | } | 561 | } |
451 | catch (WebException e) | 562 | catch (WebException e) |
452 | { | 563 | { |
@@ -454,57 +565,127 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | |||
454 | { | 565 | { |
455 | throw; | 566 | throw; |
456 | } | 567 | } |
457 | response = (HttpWebResponse)e.Response; | 568 | |
569 | HttpWebResponse response = (HttpWebResponse)e.Response; | ||
570 | |||
571 | Status = (int)response.StatusCode; | ||
572 | ResponseBody = response.StatusDescription; | ||
573 | _finished = true; | ||
458 | } | 574 | } |
575 | } | ||
576 | catch (Exception e) | ||
577 | { | ||
578 | // m_log.Debug( | ||
579 | // string.Format("[SCRIPTS HTTP REQUESTS]: Exception on request to {0} for {1} ", Url, ItemID), e); | ||
459 | 580 | ||
460 | Status = (int)response.StatusCode; | 581 | Status = (int)OSHttpStatusCode.ClientErrorJoker; |
582 | ResponseBody = e.Message; | ||
583 | _finished = true; | ||
584 | } | ||
585 | } | ||
461 | 586 | ||
462 | Stream resStream = response.GetResponseStream(); | 587 | private void ResponseCallback(IAsyncResult ar) |
588 | { | ||
589 | HttpWebResponse response = null; | ||
463 | 590 | ||
464 | do | 591 | try |
592 | { | ||
593 | try | ||
465 | { | 594 | { |
466 | // fill the buffer with data | 595 | response = (HttpWebResponse)Request.EndGetResponse(ar); |
467 | count = resStream.Read(buf, 0, buf.Length); | 596 | } |
468 | 597 | catch (WebException e) | |
469 | // make sure we read some data | 598 | { |
470 | if (count != 0) | 599 | if (e.Status != WebExceptionStatus.ProtocolError) |
471 | { | 600 | { |
472 | // translate from bytes to ASCII text | 601 | throw; |
473 | tempString = Util.UTF8.GetString(buf, 0, count); | ||
474 | |||
475 | // continue building the string | ||
476 | sb.Append(tempString); | ||
477 | } | 602 | } |
478 | } while (count > 0); // any more data to read? | ||
479 | 603 | ||
480 | ResponseBody = sb.ToString(); | 604 | response = (HttpWebResponse)e.Response; |
605 | } | ||
606 | |||
607 | Status = (int)response.StatusCode; | ||
608 | |||
609 | using (Stream stream = response.GetResponseStream()) | ||
610 | { | ||
611 | StreamReader reader = new StreamReader(stream, Encoding.UTF8); | ||
612 | ResponseBody = reader.ReadToEnd(); | ||
613 | } | ||
481 | } | 614 | } |
482 | catch (Exception e) | 615 | catch (Exception e) |
483 | { | 616 | { |
484 | Status = (int)OSHttpStatusCode.ClientErrorJoker; | 617 | Status = (int)OSHttpStatusCode.ClientErrorJoker; |
485 | ResponseBody = e.Message; | 618 | ResponseBody = e.Message; |
486 | 619 | ||
487 | _finished = true; | 620 | // m_log.Debug( |
488 | return; | 621 | // string.Format("[SCRIPTS HTTP REQUESTS]: Exception on response to {0} for {1} ", Url, ItemID), e); |
489 | } | 622 | } |
490 | finally | 623 | finally |
491 | { | 624 | { |
492 | if (response != null) | 625 | if (response != null) |
493 | response.Close(); | 626 | response.Close(); |
627 | |||
628 | // We need to resubmit | ||
629 | if ( | ||
630 | (Status == (int)HttpStatusCode.MovedPermanently | ||
631 | || Status == (int)HttpStatusCode.Found | ||
632 | || Status == (int)HttpStatusCode.SeeOther | ||
633 | || Status == (int)HttpStatusCode.TemporaryRedirect)) | ||
634 | { | ||
635 | if (Redirects >= MaxRedirects) | ||
636 | { | ||
637 | Status = (int)OSHttpStatusCode.ClientErrorJoker; | ||
638 | ResponseBody = "Number of redirects exceeded max redirects"; | ||
639 | _finished = true; | ||
640 | } | ||
641 | else | ||
642 | { | ||
643 | string location = response.Headers["Location"]; | ||
644 | |||
645 | if (location == null) | ||
646 | { | ||
647 | Status = (int)OSHttpStatusCode.ClientErrorJoker; | ||
648 | ResponseBody = "HTTP redirect code but no location header"; | ||
649 | _finished = true; | ||
650 | } | ||
651 | else if (!RequestModule.CheckAllowed(new Uri(location))) | ||
652 | { | ||
653 | Status = (int)OSHttpStatusCode.ClientErrorJoker; | ||
654 | ResponseBody = "URL from HTTP redirect blocked: " + location; | ||
655 | _finished = true; | ||
656 | } | ||
657 | else | ||
658 | { | ||
659 | Status = 0; | ||
660 | Url = response.Headers["Location"]; | ||
661 | Redirects++; | ||
662 | ResponseBody = null; | ||
663 | |||
664 | // m_log.DebugFormat("Redirecting to [{0}]", Url); | ||
665 | |||
666 | Process(); | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | else | ||
671 | { | ||
672 | _finished = true; | ||
673 | } | ||
494 | } | 674 | } |
675 | } | ||
495 | 676 | ||
496 | _finished = true; | 677 | private void TimeoutCallback(object state, bool timedOut) |
678 | { | ||
679 | if (timedOut) | ||
680 | Request.Abort(); | ||
497 | } | 681 | } |
498 | 682 | ||
499 | public void Stop() | 683 | public void Stop() |
500 | { | 684 | { |
501 | try | 685 | // m_log.DebugFormat("[SCRIPTS HTTP REQUESTS]: Stopping request to {0} for {1} ", Url, ItemID); |
502 | { | 686 | |
503 | httpThread.Abort(); | 687 | if (Request != null) |
504 | } | 688 | Request.Abort(); |
505 | catch (Exception) | ||
506 | { | ||
507 | } | ||
508 | } | 689 | } |
509 | } | 690 | } |
510 | } | 691 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs new file mode 100644 index 0000000..d22487e --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs | |||
@@ -0,0 +1,199 @@ | |||
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.Generic; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Reflection; | ||
33 | using System.Runtime.Serialization; | ||
34 | using System.Text; | ||
35 | using System.Threading; | ||
36 | using log4net.Config; | ||
37 | using NUnit.Framework; | ||
38 | using OpenMetaverse; | ||
39 | using OpenMetaverse.Assets; | ||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Region.CoreModules.Scripting.HttpRequest; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | using OpenSim.Tests.Common; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests | ||
46 | { | ||
47 | class TestWebRequestCreate : IWebRequestCreate | ||
48 | { | ||
49 | public TestWebRequest NextRequest { get; set; } | ||
50 | |||
51 | public WebRequest Create(Uri uri) | ||
52 | { | ||
53 | // NextRequest.RequestUri = uri; | ||
54 | |||
55 | return NextRequest; | ||
56 | |||
57 | // return new TestWebRequest(new SerializationInfo(typeof(TestWebRequest), new FormatterConverter()), new StreamingContext()); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | class TestWebRequest : WebRequest | ||
62 | { | ||
63 | public override string ContentType { get; set; } | ||
64 | public override string Method { get; set; } | ||
65 | |||
66 | public Func<IAsyncResult, WebResponse> OnEndGetResponse { get; set; } | ||
67 | |||
68 | public TestWebRequest() : base() | ||
69 | { | ||
70 | // Console.WriteLine("created"); | ||
71 | } | ||
72 | |||
73 | // public TestWebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext) | ||
74 | // : base(serializationInfo, streamingContext) | ||
75 | // { | ||
76 | // Console.WriteLine("created"); | ||
77 | // } | ||
78 | |||
79 | public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state) | ||
80 | { | ||
81 | // Console.WriteLine("bish"); | ||
82 | TestAsyncResult tasr = new TestAsyncResult(); | ||
83 | callback(tasr); | ||
84 | |||
85 | return tasr; | ||
86 | } | ||
87 | |||
88 | public override WebResponse EndGetResponse(IAsyncResult asyncResult) | ||
89 | { | ||
90 | // Console.WriteLine("bosh"); | ||
91 | return OnEndGetResponse(asyncResult); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | class TestHttpWebResponse : HttpWebResponse | ||
96 | { | ||
97 | public string Response { get; set; } | ||
98 | |||
99 | #pragma warning disable 0618 | ||
100 | public TestHttpWebResponse(SerializationInfo serializationInfo, StreamingContext streamingContext) | ||
101 | : base(serializationInfo, streamingContext) {} | ||
102 | #pragma warning restore 0618 | ||
103 | |||
104 | public override Stream GetResponseStream() | ||
105 | { | ||
106 | return new MemoryStream(Encoding.UTF8.GetBytes(Response)); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | class TestAsyncResult : IAsyncResult | ||
111 | { | ||
112 | WaitHandle m_wh = new ManualResetEvent(true); | ||
113 | |||
114 | object IAsyncResult.AsyncState | ||
115 | { | ||
116 | get { | ||
117 | throw new System.NotImplementedException (); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | WaitHandle IAsyncResult.AsyncWaitHandle | ||
122 | { | ||
123 | get { return m_wh; } | ||
124 | } | ||
125 | |||
126 | bool IAsyncResult.CompletedSynchronously | ||
127 | { | ||
128 | get { return false; } | ||
129 | } | ||
130 | |||
131 | bool IAsyncResult.IsCompleted | ||
132 | { | ||
133 | get { return true; } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Test script http request code. | ||
139 | /// </summary> | ||
140 | /// <remarks> | ||
141 | /// This class uses some very hacky workarounds in order to mock HttpWebResponse which are Mono dependent (though | ||
142 | /// alternative code can be written to make this work for Windows). However, the value of being able to | ||
143 | /// regression test this kind of code is very high. | ||
144 | /// </remarks> | ||
145 | [TestFixture] | ||
146 | public class ScriptsHttpRequestsTests : OpenSimTestCase | ||
147 | { | ||
148 | /// <summary> | ||
149 | /// Test what happens when we get a 404 response from a call. | ||
150 | /// </summary> | ||
151 | // [Test] | ||
152 | public void Test404Response() | ||
153 | { | ||
154 | TestHelpers.InMethod(); | ||
155 | TestHelpers.EnableLogging(); | ||
156 | |||
157 | if (!Util.IsPlatformMono) | ||
158 | Assert.Ignore("Ignoring test since can only currently run on Mono"); | ||
159 | |||
160 | string rawResponse = "boom"; | ||
161 | |||
162 | TestWebRequestCreate twrc = new TestWebRequestCreate(); | ||
163 | |||
164 | TestWebRequest twr = new TestWebRequest(); | ||
165 | //twr.OnEndGetResponse += ar => new TestHttpWebResponse(null, new StreamingContext()); | ||
166 | twr.OnEndGetResponse += ar => | ||
167 | { | ||
168 | SerializationInfo si = new SerializationInfo(typeof(HttpWebResponse), new FormatterConverter()); | ||
169 | StreamingContext sc = new StreamingContext(); | ||
170 | // WebHeaderCollection headers = new WebHeaderCollection(); | ||
171 | // si.AddValue("m_HttpResponseHeaders", headers); | ||
172 | si.AddValue("uri", new Uri("test://arrg")); | ||
173 | // si.AddValue("m_Certificate", null); | ||
174 | si.AddValue("version", HttpVersion.Version11); | ||
175 | si.AddValue("statusCode", HttpStatusCode.NotFound); | ||
176 | si.AddValue("contentLength", 0); | ||
177 | si.AddValue("method", "GET"); | ||
178 | si.AddValue("statusDescription", "Not Found"); | ||
179 | si.AddValue("contentType", null); | ||
180 | si.AddValue("cookieCollection", new CookieCollection()); | ||
181 | |||
182 | TestHttpWebResponse thwr = new TestHttpWebResponse(si, sc); | ||
183 | thwr.Response = rawResponse; | ||
184 | |||
185 | throw new WebException("no message", null, WebExceptionStatus.ProtocolError, thwr); | ||
186 | }; | ||
187 | |||
188 | twrc.NextRequest = twr; | ||
189 | |||
190 | WebRequest.RegisterPrefix("test", twrc); | ||
191 | HttpRequestClass hr = new HttpRequestClass(); | ||
192 | hr.Url = "test://something"; | ||
193 | hr.SendRequest(); | ||
194 | |||
195 | Assert.That(hr.Status, Is.EqualTo((int)HttpStatusCode.NotFound)); | ||
196 | Assert.That(hr.ResponseBody, Is.EqualTo(rawResponse)); | ||
197 | } | ||
198 | } | ||
199 | } \ No newline at end of file | ||