2007-12-06 00:58:28 +00:00
|
|
|
/*
|
2008-07-21 21:12:59 +00:00
|
|
|
* Copyright (c) 2007-2008, openmetaverse.org
|
2007-12-06 00:58:28 +00:00
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* - Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* - Redistributions of source code must retain the above copyright notice, this
|
|
|
|
|
* list of conditions and the following disclaimer.
|
2008-07-21 21:12:59 +00:00
|
|
|
* - Neither the name of the openmetaverse.org nor the names
|
2007-12-06 00:58:28 +00:00
|
|
|
* of its contributors may be used to endorse or promote products derived from
|
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
2008-07-21 21:12:59 +00:00
|
|
|
using OpenMetaverse.StructuredData;
|
2007-12-06 00:58:28 +00:00
|
|
|
|
2008-07-21 21:12:59 +00:00
|
|
|
namespace OpenMetaverse.Capabilities
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
|
|
|
|
public class CapsClient
|
|
|
|
|
{
|
|
|
|
|
public delegate void ProgressCallback(CapsClient client, long bytesReceived, long bytesSent,
|
|
|
|
|
long totalBytesToReceive, long totalBytesToSend);
|
|
|
|
|
public delegate void CompleteCallback(CapsClient client, LLSD result, Exception error);
|
|
|
|
|
|
|
|
|
|
public ProgressCallback OnProgress;
|
|
|
|
|
public CompleteCallback OnComplete;
|
|
|
|
|
|
|
|
|
|
public IWebProxy Proxy;
|
|
|
|
|
public object UserData;
|
|
|
|
|
|
|
|
|
|
protected CapsBase _Client;
|
|
|
|
|
protected byte[] _PostData;
|
|
|
|
|
protected string _ContentType;
|
|
|
|
|
|
|
|
|
|
public CapsClient(Uri capability)
|
|
|
|
|
{
|
|
|
|
|
_Client = new CapsBase(capability);
|
2008-05-05 23:41:41 +00:00
|
|
|
_Client.DownloadProgressChanged += new CapsBase.DownloadProgressChangedEventHandler(Client_DownloadProgressChanged);
|
|
|
|
|
_Client.UploadProgressChanged += new CapsBase.UploadProgressChangedEventHandler(Client_UploadProgressChanged);
|
|
|
|
|
_Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted);
|
|
|
|
|
_Client.DownloadStringCompleted += new CapsBase.DownloadStringCompletedEventHandler(Client_DownloadStringCompleted);
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartRequest()
|
|
|
|
|
{
|
|
|
|
|
StartRequest(null, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartRequest(LLSD llsd)
|
|
|
|
|
{
|
|
|
|
|
byte[] postData = LLSDParser.SerializeXmlBytes(llsd);
|
|
|
|
|
StartRequest(postData, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartRequest(byte[] postData)
|
|
|
|
|
{
|
|
|
|
|
StartRequest(postData, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartRequest(byte[] postData, string contentType)
|
|
|
|
|
{
|
|
|
|
|
_PostData = postData;
|
|
|
|
|
_ContentType = contentType;
|
|
|
|
|
|
|
|
|
|
if (_Client.IsBusy)
|
|
|
|
|
{
|
2008-05-06 23:57:26 +00:00
|
|
|
Logger.Log("New CAPS request to " + _Client.Location +
|
2007-12-21 06:38:19 +00:00
|
|
|
" initiated, closing previous request", Helpers.LogLevel.Warning);
|
2007-12-06 00:58:28 +00:00
|
|
|
_Client.CancelAsync();
|
|
|
|
|
}
|
2007-12-21 06:38:19 +00:00
|
|
|
else
|
|
|
|
|
{
|
2008-05-06 23:57:26 +00:00
|
|
|
Logger.DebugLog("New CAPS request to " + _Client.Location + " initiated");
|
2007-12-21 06:38:19 +00:00
|
|
|
}
|
2008-07-29 05:17:01 +00:00
|
|
|
|
|
|
|
|
_Client.Headers.Clear();
|
|
|
|
|
|
2007-12-06 00:58:28 +00:00
|
|
|
// Proxy
|
|
|
|
|
if (Proxy != null)
|
|
|
|
|
_Client.Proxy = Proxy;
|
|
|
|
|
|
|
|
|
|
// Content-Type
|
|
|
|
|
if (!String.IsNullOrEmpty(contentType))
|
|
|
|
|
_Client.Headers.Add(HttpRequestHeader.ContentType, contentType);
|
|
|
|
|
else
|
|
|
|
|
_Client.Headers.Add(HttpRequestHeader.ContentType, "application/xml");
|
|
|
|
|
|
|
|
|
|
if (postData == null)
|
|
|
|
|
_Client.DownloadStringAsync(_Client.Location);
|
|
|
|
|
else
|
|
|
|
|
_Client.UploadDataAsync(_Client.Location, postData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Cancel()
|
|
|
|
|
{
|
|
|
|
|
if (_Client.IsBusy)
|
|
|
|
|
_Client.CancelAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Callback Handlers
|
|
|
|
|
|
2008-05-05 23:41:41 +00:00
|
|
|
private void Client_DownloadProgressChanged(object sender, CapsBase.DownloadProgressChangedEventArgs e)
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
|
|
|
|
if (OnProgress != null)
|
|
|
|
|
{
|
|
|
|
|
try { OnProgress(this, e.BytesReceived, 0, e.TotalBytesToReceive, 0); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-05 23:41:41 +00:00
|
|
|
private void Client_UploadProgressChanged(object sender, CapsBase.UploadProgressChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (OnProgress != null)
|
|
|
|
|
{
|
|
|
|
|
try { OnProgress(this, e.BytesReceived, e.BytesSent, e.TotalBytesToReceive, e.TotalBytesToSend); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2008-05-05 23:41:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Client_UploadDataCompleted(object sender, CapsBase.UploadDataCompletedEventArgs e)
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
|
|
|
|
if (OnComplete != null && !e.Cancelled)
|
|
|
|
|
{
|
|
|
|
|
if (e.Error == null)
|
|
|
|
|
{
|
|
|
|
|
LLSD result = LLSDParser.DeserializeXml(e.Result);
|
|
|
|
|
|
|
|
|
|
try { OnComplete(this, result, e.Error); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// Some error occurred, try to figure out what happened
|
|
|
|
|
HttpStatusCode code = HttpStatusCode.OK;
|
2008-08-15 22:10:54 +00:00
|
|
|
if (e.Error is WebException && ((WebException)e.Error).Response != null)
|
2008-08-12 22:38:02 +00:00
|
|
|
code = ((HttpWebResponse)((WebException)e.Error).Response).StatusCode;
|
|
|
|
|
|
|
|
|
|
if (code == HttpStatusCode.BadGateway)
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// This is not good (server) protocol design, but it's normal.
|
|
|
|
|
// The CAPS server is a proxy that connects to a Squid
|
|
|
|
|
// cache which will time out periodically. The CAPS server
|
|
|
|
|
// interprets this as a generic error and returns a 502 to us
|
|
|
|
|
// that we ignore
|
2007-12-06 00:58:28 +00:00
|
|
|
StartRequest(_PostData, _ContentType);
|
|
|
|
|
}
|
2008-08-12 22:38:02 +00:00
|
|
|
else if (code != HttpStatusCode.OK)
|
|
|
|
|
{
|
|
|
|
|
// Status code was set to something unknown, this is a failure
|
|
|
|
|
Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, code));
|
|
|
|
|
|
|
|
|
|
try { OnComplete(this, null, e.Error); }
|
|
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
|
|
|
|
}
|
2007-12-06 00:58:28 +00:00
|
|
|
else
|
|
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// Status code was not set, some other error occurred. This is a failure
|
2008-05-06 23:57:26 +00:00
|
|
|
Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, e.Error.Message));
|
|
|
|
|
|
2007-12-06 00:58:28 +00:00
|
|
|
try { OnComplete(this, null, e.Error); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-05-05 23:41:41 +00:00
|
|
|
else if (e.Cancelled)
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
2008-05-06 23:57:26 +00:00
|
|
|
Logger.DebugLog("Capability action at " + _Client.Location + " cancelled");
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-05 23:41:41 +00:00
|
|
|
private void Client_DownloadStringCompleted(object sender, CapsBase.DownloadStringCompletedEventArgs e)
|
2007-12-06 00:58:28 +00:00
|
|
|
{
|
|
|
|
|
if (OnComplete != null && !e.Cancelled)
|
|
|
|
|
{
|
2007-12-18 19:23:46 +00:00
|
|
|
if (e.Error == null)
|
|
|
|
|
{
|
|
|
|
|
LLSD result = LLSDParser.DeserializeXml(e.Result);
|
2007-12-06 00:58:28 +00:00
|
|
|
|
2007-12-18 19:23:46 +00:00
|
|
|
try { OnComplete(this, result, e.Error); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2007-12-18 19:23:46 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// Some error occurred, try to figure out what happened
|
|
|
|
|
HttpStatusCode code = HttpStatusCode.OK;
|
2008-08-15 22:10:54 +00:00
|
|
|
if (e.Error is WebException && ((WebException)e.Error).Response != null)
|
2008-08-12 22:38:02 +00:00
|
|
|
code = ((HttpWebResponse)((WebException)e.Error).Response).StatusCode;
|
|
|
|
|
|
|
|
|
|
if (code == HttpStatusCode.BadGateway)
|
2007-12-18 19:23:46 +00:00
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// This is not good (server) protocol design, but it's normal.
|
|
|
|
|
// The CAPS server is a proxy that connects to a Squid
|
|
|
|
|
// cache which will time out periodically. The CAPS server
|
|
|
|
|
// interprets this as a generic error and returns a 502 to us
|
|
|
|
|
// that we ignore
|
2007-12-18 19:23:46 +00:00
|
|
|
StartRequest(_PostData, _ContentType);
|
|
|
|
|
}
|
2008-08-12 22:38:02 +00:00
|
|
|
else if (code != HttpStatusCode.OK)
|
|
|
|
|
{
|
|
|
|
|
// Status code was set to something unknown, this is a failure
|
|
|
|
|
Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, code));
|
|
|
|
|
|
|
|
|
|
try { OnComplete(this, null, e.Error); }
|
|
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
|
|
|
|
}
|
2007-12-18 19:23:46 +00:00
|
|
|
else
|
|
|
|
|
{
|
2008-08-12 22:38:02 +00:00
|
|
|
// Status code was not set, some other error occurred. This is a failure
|
|
|
|
|
Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, e.Error.Message));
|
|
|
|
|
|
2007-12-18 19:23:46 +00:00
|
|
|
try { OnComplete(this, null, e.Error); }
|
2008-05-06 23:57:26 +00:00
|
|
|
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
|
2007-12-18 19:23:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
2007-12-21 06:25:13 +00:00
|
|
|
else if (e.Cancelled)
|
|
|
|
|
{
|
2008-05-06 23:57:26 +00:00
|
|
|
Logger.DebugLog("Capability action at " + _Client.Location + " cancelled");
|
2007-12-21 06:25:13 +00:00
|
|
|
}
|
2007-12-06 00:58:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion Callback Handlers
|
|
|
|
|
}
|
|
|
|
|
}
|