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/Framework/Communications/RestClient.cs | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz |
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/RestClient.cs (renamed from OpenSim/Framework/Communications/RestClient.cs) | 314 |
1 files changed, 276 insertions, 38 deletions
diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/RestClient.cs index 97b3b60..7080ca5 100644 --- a/OpenSim/Framework/Communications/RestClient.cs +++ b/OpenSim/Framework/RestClient.cs | |||
@@ -35,7 +35,9 @@ using System.Threading; | |||
35 | using System.Web; | 35 | using System.Web; |
36 | using log4net; | 36 | using log4net; |
37 | 37 | ||
38 | namespace OpenSim.Framework.Communications | 38 | using OpenSim.Framework.ServiceAuth; |
39 | |||
40 | namespace OpenSim.Framework | ||
39 | { | 41 | { |
40 | /// <summary> | 42 | /// <summary> |
41 | /// Implementation of a generic REST client | 43 | /// Implementation of a generic REST client |
@@ -54,7 +56,7 @@ namespace OpenSim.Framework.Communications | |||
54 | /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be | 56 | /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be |
55 | /// invoked by the caller in either synchronous mode or asynchronous modes. | 57 | /// invoked by the caller in either synchronous mode or asynchronous modes. |
56 | /// </remarks> | 58 | /// </remarks> |
57 | public class RestClient | 59 | public class RestClient : IDisposable |
58 | { | 60 | { |
59 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
60 | 62 | ||
@@ -88,7 +90,7 @@ namespace OpenSim.Framework.Communications | |||
88 | private byte[] _readbuf; | 90 | private byte[] _readbuf; |
89 | 91 | ||
90 | /// <summary> | 92 | /// <summary> |
91 | /// MemoryStream representing the resultiong resource | 93 | /// MemoryStream representing the resulting resource |
92 | /// </summary> | 94 | /// </summary> |
93 | private Stream _resource; | 95 | private Stream _resource; |
94 | 96 | ||
@@ -146,6 +148,33 @@ namespace OpenSim.Framework.Communications | |||
146 | 148 | ||
147 | #endregion constructors | 149 | #endregion constructors |
148 | 150 | ||
151 | |||
152 | #region Dispose | ||
153 | |||
154 | private bool disposed = false; | ||
155 | |||
156 | public void Dispose() | ||
157 | { | ||
158 | Dispose(true); | ||
159 | GC.SuppressFinalize(this); | ||
160 | } | ||
161 | |||
162 | protected virtual void Dispose(bool disposing) | ||
163 | { | ||
164 | if (disposed) | ||
165 | return; | ||
166 | |||
167 | if (disposing) | ||
168 | { | ||
169 | _resource.Dispose(); | ||
170 | } | ||
171 | |||
172 | disposed = true; | ||
173 | } | ||
174 | |||
175 | #endregion Dispose | ||
176 | |||
177 | |||
149 | /// <summary> | 178 | /// <summary> |
150 | /// Add a path element to the query, e.g. assets | 179 | /// Add a path element to the query, e.g. assets |
151 | /// </summary> | 180 | /// </summary> |
@@ -299,6 +328,14 @@ namespace OpenSim.Framework.Communications | |||
299 | /// </summary> | 328 | /// </summary> |
300 | public Stream Request() | 329 | public Stream Request() |
301 | { | 330 | { |
331 | return Request(null); | ||
332 | } | ||
333 | |||
334 | /// <summary> | ||
335 | /// Perform a synchronous request | ||
336 | /// </summary> | ||
337 | public Stream Request(IServiceAuth auth) | ||
338 | { | ||
302 | lock (_lock) | 339 | lock (_lock) |
303 | { | 340 | { |
304 | _request = (HttpWebRequest) WebRequest.Create(buildUri()); | 341 | _request = (HttpWebRequest) WebRequest.Create(buildUri()); |
@@ -307,44 +344,54 @@ namespace OpenSim.Framework.Communications | |||
307 | _request.Timeout = 200000; | 344 | _request.Timeout = 200000; |
308 | _request.Method = RequestMethod; | 345 | _request.Method = RequestMethod; |
309 | _asyncException = null; | 346 | _asyncException = null; |
347 | if (auth != null) | ||
348 | auth.AddAuthorization(_request.Headers); | ||
349 | |||
350 | int reqnum = WebUtil.RequestNumber++; | ||
351 | if (WebUtil.DebugLevel >= 3) | ||
352 | m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); | ||
310 | 353 | ||
311 | // IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); | 354 | // IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); |
355 | |||
312 | try | 356 | try |
313 | { | 357 | { |
314 | _response = (HttpWebResponse) _request.GetResponse(); | 358 | using (_response = (HttpWebResponse) _request.GetResponse()) |
359 | { | ||
360 | using (Stream src = _response.GetResponseStream()) | ||
361 | { | ||
362 | int length = src.Read(_readbuf, 0, BufferSize); | ||
363 | while (length > 0) | ||
364 | { | ||
365 | _resource.Write(_readbuf, 0, length); | ||
366 | length = src.Read(_readbuf, 0, BufferSize); | ||
367 | } | ||
368 | |||
369 | // TODO! Implement timeout, without killing the server | ||
370 | // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted | ||
371 | //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); | ||
372 | |||
373 | // _allDone.WaitOne(); | ||
374 | } | ||
375 | } | ||
315 | } | 376 | } |
316 | catch (WebException e) | 377 | catch (WebException e) |
317 | { | 378 | { |
318 | HttpWebResponse errorResponse = e.Response as HttpWebResponse; | 379 | using (HttpWebResponse errorResponse = e.Response as HttpWebResponse) |
319 | if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode) | ||
320 | { | ||
321 | m_log.Warn("[REST CLIENT] Resource not found (404)"); | ||
322 | } | ||
323 | else | ||
324 | { | 380 | { |
325 | m_log.Error("[REST CLIENT] Error fetching resource from server " + _request.Address.ToString()); | 381 | if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode) |
326 | m_log.Debug(e.ToString()); | 382 | { |
383 | // This is often benign. E.g., requesting a missing asset will return 404. | ||
384 | m_log.DebugFormat("[REST CLIENT] Resource not found (404): {0}", _request.Address.ToString()); | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | m_log.Error(string.Format("[REST CLIENT] Error fetching resource from server: {0} ", _request.Address.ToString()), e); | ||
389 | } | ||
327 | } | 390 | } |
328 | 391 | ||
329 | return null; | 392 | return null; |
330 | } | 393 | } |
331 | 394 | ||
332 | Stream src = _response.GetResponseStream(); | ||
333 | int length = src.Read(_readbuf, 0, BufferSize); | ||
334 | while (length > 0) | ||
335 | { | ||
336 | _resource.Write(_readbuf, 0, length); | ||
337 | length = src.Read(_readbuf, 0, BufferSize); | ||
338 | } | ||
339 | |||
340 | |||
341 | // TODO! Implement timeout, without killing the server | ||
342 | // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted | ||
343 | //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); | ||
344 | |||
345 | // _allDone.WaitOne(); | ||
346 | if (_response != null) | ||
347 | _response.Close(); | ||
348 | if (_asyncException != null) | 395 | if (_asyncException != null) |
349 | throw _asyncException; | 396 | throw _asyncException; |
350 | 397 | ||
@@ -354,11 +401,14 @@ namespace OpenSim.Framework.Communications | |||
354 | _resource.Seek(0, SeekOrigin.Begin); | 401 | _resource.Seek(0, SeekOrigin.Begin); |
355 | } | 402 | } |
356 | 403 | ||
404 | if (WebUtil.DebugLevel >= 5) | ||
405 | WebUtil.LogResponseDetail(reqnum, _resource); | ||
406 | |||
357 | return _resource; | 407 | return _resource; |
358 | } | 408 | } |
359 | } | 409 | } |
360 | 410 | ||
361 | public Stream Request(Stream src) | 411 | public Stream Request(Stream src, IServiceAuth auth) |
362 | { | 412 | { |
363 | _request = (HttpWebRequest) WebRequest.Create(buildUri()); | 413 | _request = (HttpWebRequest) WebRequest.Create(buildUri()); |
364 | _request.KeepAlive = false; | 414 | _request.KeepAlive = false; |
@@ -367,24 +417,58 @@ namespace OpenSim.Framework.Communications | |||
367 | _request.Method = RequestMethod; | 417 | _request.Method = RequestMethod; |
368 | _asyncException = null; | 418 | _asyncException = null; |
369 | _request.ContentLength = src.Length; | 419 | _request.ContentLength = src.Length; |
420 | if (auth != null) | ||
421 | auth.AddAuthorization(_request.Headers); | ||
370 | 422 | ||
371 | m_log.InfoFormat("[REST]: Request Length {0}", _request.ContentLength); | ||
372 | m_log.InfoFormat("[REST]: Sending Web Request {0}", buildUri()); | ||
373 | src.Seek(0, SeekOrigin.Begin); | 423 | src.Seek(0, SeekOrigin.Begin); |
374 | m_log.Info("[REST]: Seek is ok"); | 424 | |
425 | int reqnum = WebUtil.RequestNumber++; | ||
426 | if (WebUtil.DebugLevel >= 3) | ||
427 | m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); | ||
428 | if (WebUtil.DebugLevel >= 5) | ||
429 | WebUtil.LogOutgoingDetail(string.Format("SEND {0}: ", reqnum), src); | ||
430 | |||
375 | Stream dst = _request.GetRequestStream(); | 431 | Stream dst = _request.GetRequestStream(); |
376 | m_log.Info("[REST]: GetRequestStream is ok"); | ||
377 | 432 | ||
378 | byte[] buf = new byte[1024]; | 433 | byte[] buf = new byte[1024]; |
379 | int length = src.Read(buf, 0, 1024); | 434 | int length = src.Read(buf, 0, 1024); |
380 | m_log.Info("[REST]: First Read is ok"); | ||
381 | while (length > 0) | 435 | while (length > 0) |
382 | { | 436 | { |
383 | dst.Write(buf, 0, length); | 437 | dst.Write(buf, 0, length); |
384 | length = src.Read(buf, 0, 1024); | 438 | length = src.Read(buf, 0, 1024); |
385 | } | 439 | } |
386 | 440 | ||
387 | _response = (HttpWebResponse) _request.GetResponse(); | 441 | try |
442 | { | ||
443 | _response = (HttpWebResponse)_request.GetResponse(); | ||
444 | } | ||
445 | catch (WebException e) | ||
446 | { | ||
447 | m_log.WarnFormat("[REST]: Request {0} {1} failed with status {2} and message {3}", | ||
448 | RequestMethod, _request.RequestUri, e.Status, e.Message); | ||
449 | return null; | ||
450 | } | ||
451 | catch (Exception e) | ||
452 | { | ||
453 | m_log.WarnFormat( | ||
454 | "[REST]: Request {0} {1} failed with exception {2} {3}", | ||
455 | RequestMethod, _request.RequestUri, e.Message, e.StackTrace); | ||
456 | return null; | ||
457 | } | ||
458 | |||
459 | if (WebUtil.DebugLevel >= 5) | ||
460 | { | ||
461 | using (Stream responseStream = _response.GetResponseStream()) | ||
462 | { | ||
463 | using (StreamReader reader = new StreamReader(responseStream)) | ||
464 | { | ||
465 | string responseStr = reader.ReadToEnd(); | ||
466 | WebUtil.LogResponseDetail(reqnum, responseStr); | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | |||
471 | _response.Close(); | ||
388 | 472 | ||
389 | // IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); | 473 | // IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); |
390 | 474 | ||
@@ -403,7 +487,7 @@ namespace OpenSim.Framework.Communications | |||
403 | /// In case, we are invoked asynchroneously this object will keep track of the state | 487 | /// In case, we are invoked asynchroneously this object will keep track of the state |
404 | /// </summary> | 488 | /// </summary> |
405 | AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state); | 489 | AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state); |
406 | Util.FireAndForget(RequestHelper, ar); | 490 | Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest"); |
407 | return ar; | 491 | return ar; |
408 | } | 492 | } |
409 | 493 | ||
@@ -423,7 +507,7 @@ namespace OpenSim.Framework.Communications | |||
423 | try | 507 | try |
424 | { | 508 | { |
425 | // Perform the operation; if sucessful set the result | 509 | // Perform the operation; if sucessful set the result |
426 | Stream s = Request(); | 510 | Stream s = Request(null); |
427 | ar.SetAsCompleted(s, false); | 511 | ar.SetAsCompleted(s, false); |
428 | } | 512 | } |
429 | catch (Exception e) | 513 | catch (Exception e) |
@@ -435,4 +519,158 @@ namespace OpenSim.Framework.Communications | |||
435 | 519 | ||
436 | #endregion Async Invocation | 520 | #endregion Async Invocation |
437 | } | 521 | } |
438 | } | 522 | |
523 | internal class SimpleAsyncResult : IAsyncResult | ||
524 | { | ||
525 | private readonly AsyncCallback m_callback; | ||
526 | |||
527 | /// <summary> | ||
528 | /// Is process completed? | ||
529 | /// </summary> | ||
530 | /// <remarks>Should really be boolean, but VolatileRead has no boolean method</remarks> | ||
531 | private byte m_completed; | ||
532 | |||
533 | /// <summary> | ||
534 | /// Did process complete synchronously? | ||
535 | /// </summary> | ||
536 | /// <remarks>I have a hard time imagining a scenario where this is the case, again, same issue about | ||
537 | /// booleans and VolatileRead as m_completed | ||
538 | /// </remarks> | ||
539 | private byte m_completedSynchronously; | ||
540 | |||
541 | private readonly object m_asyncState; | ||
542 | private ManualResetEvent m_waitHandle; | ||
543 | private Exception m_exception; | ||
544 | |||
545 | internal SimpleAsyncResult(AsyncCallback cb, object state) | ||
546 | { | ||
547 | m_callback = cb; | ||
548 | m_asyncState = state; | ||
549 | m_completed = 0; | ||
550 | m_completedSynchronously = 1; | ||
551 | } | ||
552 | |||
553 | #region IAsyncResult Members | ||
554 | |||
555 | public object AsyncState | ||
556 | { | ||
557 | get { return m_asyncState; } | ||
558 | } | ||
559 | |||
560 | public WaitHandle AsyncWaitHandle | ||
561 | { | ||
562 | get | ||
563 | { | ||
564 | if (m_waitHandle == null) | ||
565 | { | ||
566 | bool done = IsCompleted; | ||
567 | ManualResetEvent mre = new ManualResetEvent(done); | ||
568 | if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null) | ||
569 | { | ||
570 | mre.Close(); | ||
571 | } | ||
572 | else | ||
573 | { | ||
574 | if (!done && IsCompleted) | ||
575 | { | ||
576 | m_waitHandle.Set(); | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | |||
581 | return m_waitHandle; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | |||
586 | public bool CompletedSynchronously | ||
587 | { | ||
588 | get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; } | ||
589 | } | ||
590 | |||
591 | |||
592 | public bool IsCompleted | ||
593 | { | ||
594 | get { return Thread.VolatileRead(ref m_completed) == 1; } | ||
595 | } | ||
596 | |||
597 | #endregion | ||
598 | |||
599 | #region class Methods | ||
600 | |||
601 | internal void SetAsCompleted(bool completedSynchronously) | ||
602 | { | ||
603 | m_completed = 1; | ||
604 | if (completedSynchronously) | ||
605 | m_completedSynchronously = 1; | ||
606 | else | ||
607 | m_completedSynchronously = 0; | ||
608 | |||
609 | SignalCompletion(); | ||
610 | } | ||
611 | |||
612 | internal void HandleException(Exception e, bool completedSynchronously) | ||
613 | { | ||
614 | m_completed = 1; | ||
615 | if (completedSynchronously) | ||
616 | m_completedSynchronously = 1; | ||
617 | else | ||
618 | m_completedSynchronously = 0; | ||
619 | m_exception = e; | ||
620 | |||
621 | SignalCompletion(); | ||
622 | } | ||
623 | |||
624 | private void SignalCompletion() | ||
625 | { | ||
626 | if (m_waitHandle != null) m_waitHandle.Set(); | ||
627 | |||
628 | if (m_callback != null) m_callback(this); | ||
629 | } | ||
630 | |||
631 | public void EndInvoke() | ||
632 | { | ||
633 | // This method assumes that only 1 thread calls EndInvoke | ||
634 | if (!IsCompleted) | ||
635 | { | ||
636 | // If the operation isn't done, wait for it | ||
637 | AsyncWaitHandle.WaitOne(); | ||
638 | AsyncWaitHandle.Close(); | ||
639 | m_waitHandle.Close(); | ||
640 | m_waitHandle = null; // Allow early GC | ||
641 | } | ||
642 | |||
643 | // Operation is done: if an exception occured, throw it | ||
644 | if (m_exception != null) throw m_exception; | ||
645 | } | ||
646 | |||
647 | #endregion | ||
648 | } | ||
649 | |||
650 | internal class AsyncResult<T> : SimpleAsyncResult | ||
651 | { | ||
652 | private T m_result = default(T); | ||
653 | |||
654 | public AsyncResult(AsyncCallback asyncCallback, Object state) : | ||
655 | base(asyncCallback, state) | ||
656 | { | ||
657 | } | ||
658 | |||
659 | public void SetAsCompleted(T result, bool completedSynchronously) | ||
660 | { | ||
661 | // Save the asynchronous operation's result | ||
662 | m_result = result; | ||
663 | |||
664 | // Tell the base class that the operation completed | ||
665 | // sucessfully (no exception) | ||
666 | base.SetAsCompleted(completedSynchronously); | ||
667 | } | ||
668 | |||
669 | public new T EndInvoke() | ||
670 | { | ||
671 | base.EndInvoke(); | ||
672 | return m_result; | ||
673 | } | ||
674 | } | ||
675 | |||
676 | } \ No newline at end of file | ||