diff --git a/OpenMetaverse.Http/CapsBase.cs b/OpenMetaverse.Http/CapsBase.cs
index 28f057a0..a30c49b6 100644
--- a/OpenMetaverse.Http/CapsBase.cs
+++ b/OpenMetaverse.Http/CapsBase.cs
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2008, openmetaverse.org
+ * Copyright (c) 2009, openmetaverse.org
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
@@ -27,529 +27,202 @@
using System;
using System.Net;
using System.IO;
+using System.Text;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
namespace OpenMetaverse.Http
{
- public class CapsBase
+ public static class CapsBase
{
- #region Callback Data Classes
+ public delegate void OpenWriteEventHandler(HttpWebRequest request);
+ public delegate void DownloadProgressEventHandler(HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive);
+ public delegate void RequestCompletedEventHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error);
- public class OpenWriteCompletedEventArgs
+ private class RequestState
{
- public Stream Result;
- public Exception Error;
- public bool Cancelled;
- public object UserState;
+ public HttpWebRequest Request;
+ public byte[] UploadData;
+ public int MillisecondsTimeout;
+ public OpenWriteEventHandler OpenWriteCallback;
+ public DownloadProgressEventHandler DownloadProgressCallback;
+ public RequestCompletedEventHandler CompletedCallback;
- public OpenWriteCompletedEventArgs(Stream result, Exception error, bool cancelled, object userState)
+ public RequestState(HttpWebRequest request, byte[] uploadData, int millisecondsTimeout, OpenWriteEventHandler openWriteCallback,
+ DownloadProgressEventHandler downloadProgressCallback, RequestCompletedEventHandler completedCallback)
{
- Result = result;
- Error = error;
- Cancelled = cancelled;
- UserState = userState;
+ Request = request;
+ UploadData = uploadData;
+ MillisecondsTimeout = millisecondsTimeout;
+ OpenWriteCallback = openWriteCallback;
+ DownloadProgressCallback = downloadProgressCallback;
+ CompletedCallback = completedCallback;
}
}
- public class UploadDataCompletedEventArgs
+ public static HttpWebRequest UploadDataAsync(Uri address, X509Certificate2 clientCert, string contentType, byte[] data,
+ int millisecondsTimeout, OpenWriteEventHandler openWriteCallback, DownloadProgressEventHandler downloadProgressCallback,
+ RequestCompletedEventHandler completedCallback)
{
- public byte[] Result;
- public Exception Error;
- public bool Cancelled;
- public object UserState;
-
- public UploadDataCompletedEventArgs(byte[] result, Exception error, bool cancelled, object userState)
- {
- Result = result;
- Error = error;
- Cancelled = cancelled;
- UserState = userState;
- }
- }
-
- public class DownloadDataCompletedEventArgs
- {
- public byte[] Result;
- public Exception Error;
- public bool Cancelled;
- public object UserState;
- }
-
- public class DownloadStringCompletedEventArgs
- {
- public Uri Address;
- public string Result;
- public Exception Error;
- public bool Cancelled;
- public object UserState;
-
- public DownloadStringCompletedEventArgs(Uri address, string result, Exception error, bool cancelled, object userState)
- {
- Address = address;
- Result = result;
- Error = error;
- Cancelled = cancelled;
- UserState = userState;
- }
- }
-
- public class DownloadProgressChangedEventArgs
- {
- public long BytesReceived;
- public int ProgressPercentage;
- public long TotalBytesToReceive;
- public object UserState;
-
- public DownloadProgressChangedEventArgs(long bytesReceived, long totalBytesToReceive, object userToken)
- {
- BytesReceived = bytesReceived;
- ProgressPercentage = (int)(((float)bytesReceived / (float)totalBytesToReceive) * 100f);
- TotalBytesToReceive = totalBytesToReceive;
- UserState = userToken;
- }
- }
-
- public class UploadProgressChangedEventArgs
- {
- public long BytesReceived;
- public long BytesSent;
- public int ProgressPercentage;
- public long TotalBytesToReceive;
- public long TotalBytesToSend;
- public object UserState;
-
- public UploadProgressChangedEventArgs(long bytesReceived, long totalBytesToReceive, long bytesSent, long totalBytesToSend, object userState)
- {
- BytesReceived = bytesReceived;
- TotalBytesToReceive = totalBytesToReceive;
- ProgressPercentage = (int)(((float)bytesSent / (float)totalBytesToSend) * 100f);
- BytesSent = bytesSent;
- TotalBytesToSend = totalBytesToSend;
- UserState = userState;
- }
- }
-
- #endregion Callback Data Classes
-
- public delegate void OpenWriteCompletedEventHandler(object sender, OpenWriteCompletedEventArgs e);
- public delegate void UploadDataCompletedEventHandler(object sender, UploadDataCompletedEventArgs e);
- public delegate void DownloadStringCompletedEventHandler(object sender, DownloadStringCompletedEventArgs e);
- public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e);
- public delegate void UploadProgressChangedEventHandler(object sender, UploadProgressChangedEventArgs e);
-
- public event OpenWriteCompletedEventHandler OpenWriteCompleted;
- public event UploadDataCompletedEventHandler UploadDataCompleted;
- public event DownloadStringCompletedEventHandler DownloadStringCompleted;
- public event DownloadProgressChangedEventHandler DownloadProgressChanged;
- public event UploadProgressChangedEventHandler UploadProgressChanged;
-
- public WebHeaderCollection Headers = new WebHeaderCollection();
- public IWebProxy Proxy;
- public X509Certificate2 ClientCertificate;
-
- public Uri Location { get { return location; } }
- public bool IsBusy { get { return isBusy; } }
- public WebHeaderCollection ResponseHeaders { get { return responseHeaders; } }
-
- protected WebHeaderCollection responseHeaders;
- protected Uri location;
- protected bool isBusy;
- protected Thread asyncThread;
- protected System.Text.Encoding encoding = System.Text.Encoding.Default;
-
- public CapsBase(Uri location, X509Certificate2 clientCert)
- {
- this.location = location;
- ClientCertificate = clientCert;
- }
-
- public void OpenWriteAsync(Uri address)
- {
- OpenWriteAsync(address, null, null);
- }
-
- public void OpenWriteAsync(Uri address, string method)
- {
- OpenWriteAsync(address, method, null);
- }
-
- public void OpenWriteAsync(Uri address, string method, object userToken)
- {
- if (address == null)
- throw new ArgumentNullException("address");
-
- SetBusy();
-
- asyncThread = new Thread(
- delegate()
- {
- WebRequest request = null;
-
- try
- {
- request = SetupRequest(address);
- Stream stream = request.GetRequestStream();
-
- OnOpenWriteCompleted(new OpenWriteCompletedEventArgs(
- stream, null, false, userToken));
- }
- catch (ThreadInterruptedException)
- {
- if (request != null)
- request.Abort();
-
- OnOpenWriteCompleted(new OpenWriteCompletedEventArgs(
- null, null, true, userToken));
- }
- catch (Exception e)
- {
- OnOpenWriteCompleted(new OpenWriteCompletedEventArgs(
- null, e, false, userToken));
- }
- }
- );
- asyncThread.IsBackground = true;
- asyncThread.Start();
- }
-
- public void UploadDataAsync(Uri address, byte[] data)
- {
- UploadDataAsync(address, null, data, null);
- }
-
- public void UploadDataAsync(Uri address, string method, byte[] data)
- {
- UploadDataAsync(address, method, data, null);
- }
-
- public void UploadDataAsync(Uri address, string method, byte[] data, object userToken)
- {
- if (address == null)
- throw new ArgumentNullException("address");
- if (data == null)
- throw new ArgumentNullException("data");
-
- SetBusy();
-
- asyncThread = new Thread(delegate(object state)
- {
- object[] args = (object[])state;
- byte[] data2;
-
- try
- {
- data2 = UploadDataCore((Uri)args[0], (string)args[1], (byte[])args[2], args[3]);
-
- OnUploadDataCompleted(
- new UploadDataCompletedEventArgs(data2, null, false, args[3]));
- }
- catch (ThreadInterruptedException)
- {
- OnUploadDataCompleted(
- new UploadDataCompletedEventArgs(null, null, true, args[3]));
- }
- catch (Exception e)
- {
- OnUploadDataCompleted(
- new UploadDataCompletedEventArgs(null, e, false, args[3]));
- }
- });
-
- object[] cbArgs = new object[] { address, method, data, userToken };
- asyncThread.IsBackground = true;
- asyncThread.Start(cbArgs);
- }
-
- public void DownloadStringAsync(Uri address)
- {
- DownloadStringAsync(address, null);
- }
-
- public void DownloadStringAsync(Uri address, object userToken)
- {
- if (address == null)
- throw new ArgumentNullException("address");
-
- SetBusy();
-
- asyncThread = new Thread(
- delegate()
- {
- try
- {
- string data = encoding.GetString(DownloadDataCore(address, userToken));
- OnDownloadStringCompleted(
- new DownloadStringCompletedEventArgs(location, data, null, false, userToken));
- }
- catch (ThreadInterruptedException)
- {
- OnDownloadStringCompleted(
- new DownloadStringCompletedEventArgs(location, null, null, true, userToken));
- }
- catch (Exception e)
- {
- OnDownloadStringCompleted(
- new DownloadStringCompletedEventArgs(location, null, e, false, userToken));
- }
- }
- );
- asyncThread.Start();
- }
-
- public void CancelAsync()
- {
- if (asyncThread == null)
- return;
-
- Thread t = asyncThread;
- CompleteAsync();
- t.Interrupt();
- }
-
- protected void CompleteAsync()
- {
- isBusy = false;
- asyncThread = null;
- }
-
- protected void SetBusy()
- {
- CheckBusy();
- isBusy = true;
- }
-
- protected void CheckBusy()
- {
- if (isBusy)
- throw new NotSupportedException("CapsBase does not support concurrent I/O operations.");
- }
-
- protected Stream ProcessResponse(WebResponse response)
- {
- responseHeaders = response.Headers;
- return response.GetResponseStream();
- }
-
- protected byte[] ReadAll(Stream stream, int length, object userToken, bool uploading)
- {
- MemoryStream ms = null;
-
- bool nolength = (length == -1);
- int size = ((nolength) ? 8192 : length);
- if (nolength)
- ms = new MemoryStream();
-
- long total = 0;
- int nread = 0;
- int offset = 0;
- byte[] buffer = new byte[size];
-
- while ((nread = stream.Read(buffer, offset, size)) != 0)
- {
- if (nolength)
- {
- ms.Write(buffer, 0, nread);
- }
- else
- {
- offset += nread;
- size -= nread;
- }
-
- if (uploading)
- {
- if (UploadProgressChanged != null)
- {
- total += nread;
- UploadProgressChanged(this, new UploadProgressChangedEventArgs(nread, length, 0, 0, userToken));
- }
- }
- else
- {
- if (DownloadProgressChanged != null)
- {
- total += nread;
- DownloadProgressChanged(this, new DownloadProgressChangedEventArgs(nread, length, userToken));
- }
- }
- }
-
- if (nolength)
- return ms.ToArray();
-
- return buffer;
- }
-
- protected WebRequest SetupRequest(Uri uri)
- {
- HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
-
- if (request == null)
- throw new ArgumentException("Could not create an HttpWebRequest from the given Uri", "address");
-
- location = uri;
-
- if (Proxy != null)
- request.Proxy = Proxy;
-
- if (ClientCertificate != null)
- request.ClientCertificates.Add(ClientCertificate);
-
+ // Create the request
+ HttpWebRequest request = SetupRequest(address, clientCert);
+ request.ContentLength = data.Length;
+ if (!String.IsNullOrEmpty(contentType))
+ request.ContentType = contentType;
request.Method = "POST";
- if (Headers != null && Headers.Count != 0)
- {
- string expect = Headers["Expect"];
- string contentType = Headers["Content-Type"];
- string accept = Headers["Accept"];
- string connection = Headers["Connection"];
- string userAgent = Headers["User-Agent"];
- string referer = Headers["Referer"];
+ // Create an object to hold all of the state for this request
+ RequestState state = new RequestState(request, data, millisecondsTimeout, openWriteCallback,
+ downloadProgressCallback, completedCallback);
- if (!String.IsNullOrEmpty(expect))
- request.Expect = expect;
+ // Start the request for a stream to upload to
+ IAsyncResult result = request.BeginGetRequestStream(OpenWrite, state);
+ // Register a timeout for the request
+ ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state, millisecondsTimeout, true);
- if (!String.IsNullOrEmpty(accept))
- request.Accept = accept;
+ return request;
+ }
- if (!String.IsNullOrEmpty(contentType))
- request.ContentType = contentType;
+ public static HttpWebRequest DownloadStringAsync(Uri address, X509Certificate2 clientCert, int millisecondsTimeout,
+ DownloadProgressEventHandler downloadProgressCallback, RequestCompletedEventHandler completedCallback)
+ {
+ // Create the request
+ HttpWebRequest request = SetupRequest(address, clientCert);
+ request.Method = "GET";
- if (!String.IsNullOrEmpty(connection))
- request.Connection = connection;
+ // Create an object to hold all of the state for this request
+ RequestState state = new RequestState(request, null, millisecondsTimeout, null, downloadProgressCallback,
+ completedCallback);
- if (!String.IsNullOrEmpty(userAgent))
- request.UserAgent = userAgent;
+ // Start the request for the remote server response
+ IAsyncResult result = request.BeginGetResponse(GetResponse, state);
+ // Register a timeout for the request
+ ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state, millisecondsTimeout, true);
- if (!String.IsNullOrEmpty(referer))
- request.Referer = referer;
- }
+ return request;
+ }
- // Disable keep-alive by default
- request.KeepAlive = false;
- // Set the closed connection (idle) time to one second
- request.ServicePoint.MaxIdleTime = 1000;
+ static HttpWebRequest SetupRequest(Uri address, X509Certificate2 clientCert)
+ {
+ if (address == null)
+ throw new ArgumentNullException("address");
+
+ // Create the request
+ HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(address);
+
+ // Add the client certificate to the request if one was given
+ if (clientCert != null)
+ request.ClientCertificates.Add(clientCert);
+
+ // Leave idle connections to this endpoint open for up to 60 seconds
+ request.ServicePoint.MaxIdleTime = 1000 * 60;
// Disable stupid Expect-100: Continue header
request.ServicePoint.Expect100Continue = false;
- // Crank up the max number of connections (default is 2!)
- request.ServicePoint.ConnectionLimit = Int32.MaxValue;
+ // Crank up the max number of connections per endpoint (default is 2!)
+ request.ServicePoint.ConnectionLimit = 20;
+ // Caps requests are never sent as trickles of data, so Nagle's
+ // coalescing algorithm won't help us
+ request.ServicePoint.UseNagleAlgorithm = false;
return request;
}
- protected WebRequest SetupRequest(Uri uri, string method)
+ static void OpenWrite(IAsyncResult ar)
{
- WebRequest request = SetupRequest(uri);
- request.Method = method;
- return request;
- }
-
- protected byte[] UploadDataCore(Uri address, string method, byte[] data, object userToken)
- {
- HttpWebRequest request = (HttpWebRequest)SetupRequest(address);
-
- // Mono insists that if you have Content-Length set, Keep-Alive must be true.
- // Otherwise the unhelpful exception of "Content-Length not set" will be thrown.
- // The Linden Lab event queue server breaks HTTP 1.1 by always replying with a
- // Connection: Close header, which will confuse the Windows .NET runtime and throw
- // a "Connection unexpectedly closed" exception. This is our cross-platform hack
- if (Utils.GetRunningRuntime() == Utils.Runtime.Mono)
- request.KeepAlive = true;
+ RequestState state = (RequestState)ar.AsyncState;
try
{
- // Content-Length
- int contentLength = data.Length;
- request.ContentLength = contentLength;
+ // Get the stream to write our upload to
+ Stream uploadStream = state.Request.EndGetRequestStream(ar);
- using (Stream stream = request.GetRequestStream())
- {
- // Most uploads are very small chunks of data, use an optimized path for these
- if (contentLength < 4096)
- {
- stream.Write(data, 0, contentLength);
- }
- else
- {
- // Upload chunks directly instead of buffering to memory
- request.AllowWriteStreamBuffering = false;
+ // Fire the callback for successfully opening the stream
+ if (state.OpenWriteCallback != null)
+ state.OpenWriteCallback(state.Request);
- MemoryStream ms = new MemoryStream(data);
+ // Write our data to the upload stream
+ uploadStream.Write(state.UploadData, 0, state.UploadData.Length);
- byte[] buffer = new byte[checked((uint)Math.Min(4096, (int)contentLength))];
- int bytesRead = 0;
-
- while ((bytesRead = ms.Read(buffer, 0, buffer.Length)) != 0)
- {
- stream.Write(buffer, 0, bytesRead);
-
- if (UploadProgressChanged != null)
- {
- UploadProgressChanged(this, new UploadProgressChangedEventArgs(0, 0, bytesRead, contentLength, userToken));
- }
- }
-
- ms.Close();
- }
- }
-
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- Stream st = ProcessResponse(response);
-
- contentLength = (int)response.ContentLength;
- return ReadAll(st, contentLength, userToken, true);
- }
- catch (ThreadInterruptedException)
- {
- if (request != null)
- request.Abort();
- throw;
- }
- }
-
- protected byte[] DownloadDataCore(Uri address, object userToken)
- {
- WebRequest request = null;
-
- try
- {
- request = SetupRequest(address, "GET");
- WebResponse response = request.GetResponse();
- Stream st = ProcessResponse(response);
- return ReadAll(st, (int)response.ContentLength, userToken, false);
- }
- catch (ThreadInterruptedException)
- {
- if (request != null)
- request.Abort();
- throw;
+ // Start the request for the remote server response
+ IAsyncResult result = state.Request.BeginGetResponse(GetResponse, state);
+ // Register a timeout for the request
+ ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, state,
+ state.MillisecondsTimeout, true);
}
catch (Exception ex)
{
- throw new WebException("An error occurred performing a WebClient request.", ex);
+ if (state.CompletedCallback != null)
+ state.CompletedCallback(state.Request, null, null, ex);
}
}
- protected virtual void OnOpenWriteCompleted(OpenWriteCompletedEventArgs args)
+ static void GetResponse(IAsyncResult ar)
{
- CompleteAsync();
- if (OpenWriteCompleted != null)
- OpenWriteCompleted(this, args);
+ RequestState state = (RequestState)ar.AsyncState;
+ HttpWebResponse response = null;
+ byte[] responseData = null;
+ Exception error = null;
+
+ try
+ {
+ response = (HttpWebResponse)state.Request.EndGetResponse(ar);
+
+ // Get the stream for downloading the response
+ Stream responseStream = response.GetResponseStream();
+
+ #region Read the response
+
+ // If Content-Length is set we create a buffer of the exact size, otherwise
+ // a MemoryStream is used to receive the response
+ bool nolength = (response.ContentLength <= 0);
+ int size = (nolength) ? 8192 : (int)response.ContentLength;
+ MemoryStream ms = (nolength) ? new MemoryStream() : null;
+ byte[] buffer = new byte[size];
+
+ int bytesRead = 0;
+ int offset = 0;
+
+ while ((bytesRead = responseStream.Read(buffer, offset, size)) != 0)
+ {
+ if (nolength)
+ {
+ ms.Write(buffer, 0, bytesRead);
+ }
+ else
+ {
+ offset += bytesRead;
+ size -= bytesRead;
+ }
+
+ // Fire the download progress callback for each chunk of received data
+ if (state.DownloadProgressCallback != null)
+ state.DownloadProgressCallback(state.Request, response, bytesRead, size);
+ }
+
+ if (nolength)
+ responseData = ms.ToArray();
+ else
+ responseData = buffer;
+
+ #endregion Read the response
+ }
+ catch (Exception ex)
+ {
+ error = ex;
+ }
+
+ if (state.CompletedCallback != null)
+ state.CompletedCallback(state.Request, response, responseData, error);
}
- protected virtual void OnUploadDataCompleted(UploadDataCompletedEventArgs args)
+ static void TimeoutCallback(object state, bool timedOut)
{
- CompleteAsync();
- if (UploadDataCompleted != null)
- UploadDataCompleted(this, args);
- }
-
- protected virtual void OnDownloadStringCompleted(DownloadStringCompletedEventArgs args)
- {
- CompleteAsync();
- if (DownloadStringCompleted != null)
- DownloadStringCompleted(this, args);
+ if (timedOut)
+ {
+ RequestState requestState = state as RequestState;
+ if (requestState != null && requestState.Request != null)
+ requestState.Request.Abort();
+ }
}
}
}
diff --git a/OpenMetaverse.Http/CapsClient.cs b/OpenMetaverse.Http/CapsClient.cs
index 3c3c2f73..3684b4e7 100644
--- a/OpenMetaverse.Http/CapsClient.cs
+++ b/OpenMetaverse.Http/CapsClient.cs
@@ -34,272 +34,154 @@ namespace OpenMetaverse.Http
{
public class CapsClient
{
- public delegate void ProgressCallback(CapsClient client, long bytesReceived, long bytesSent,
- long totalBytesToReceive, long totalBytesToSend);
+ public delegate void DownloadProgressCallback(CapsClient client, int bytesReceived, int totalBytesToReceive);
public delegate void CompleteCallback(CapsClient client, OSD result, Exception error);
- public event ProgressCallback OnProgress;
+ public event DownloadProgressCallback OnDownloadProgress;
public event CompleteCallback OnComplete;
- public IWebProxy Proxy;
public object UserData;
- protected CapsBase _Client;
+ protected Uri _Address;
protected byte[] _PostData;
+ protected X509Certificate2 _ClientCert;
protected string _ContentType;
+ protected HttpWebRequest _Request;
+ protected OSD _Response;
+ protected AutoResetEvent _ResponseEvent = new AutoResetEvent(false);
public CapsClient(Uri capability)
+ : this(capability, null)
{
- Init(capability, null);
}
public CapsClient(Uri capability, X509Certificate2 clientCert)
{
- Init(capability, clientCert);
+ _Address = capability;
+ _ClientCert = clientCert;
}
- void Init(Uri capability, X509Certificate2 clientCert)
+ public void BeginGetResponse(int millisecondsTimeout)
{
- _Client = new CapsBase(capability, clientCert);
- _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);
+ BeginGetResponse(null, null, millisecondsTimeout);
}
- public void BeginGetResponse()
+ public void BeginGetResponse(OSD data, OSDFormat format, int millisecondsTimeout)
{
- BeginGetResponse(null, null);
- }
+ byte[] postData;
+ string contentType;
- public void BeginGetResponse(OSD data)
- {
- byte[] postData = OSDParser.SerializeLLSDXmlBytes(data);
- BeginGetResponse(postData, null);
- }
-
- public void BeginGetResponse(OSD data, bool json)
- {
- if (json)
+ switch (format)
{
- byte[] postData = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data));
- BeginGetResponse(postData, "application/json");
- }
- else
- {
- BeginGetResponse(data);
+ case OSDFormat.Xml:
+ postData = OSDParser.SerializeLLSDXmlBytes(data);
+ contentType = "application/llsd+xml";
+ break;
+ case OSDFormat.Binary:
+ postData = OSDParser.SerializeLLSDBinary(data);
+ contentType = "application/llsd+binary";
+ break;
+ case OSDFormat.Json:
+ default:
+ postData = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data));
+ contentType = "application/llsd+json";
+ break;
}
+
+ BeginGetResponse(postData, contentType, millisecondsTimeout);
}
- public void BeginGetResponse(byte[] postData)
- {
- BeginGetResponse(postData, null);
- }
-
- public void BeginGetResponse(byte[] postData, string contentType)
+ public void BeginGetResponse(byte[] postData, string contentType, int millisecondsTimeout)
{
_PostData = postData;
_ContentType = contentType;
- if (_Client.IsBusy)
- _Client.CancelAsync();
-
- _Client.Headers.Clear();
-
- // 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 (_Request != null)
+ {
+ _Request.Abort();
+ _Request = null;
+ }
if (postData == null)
- _Client.DownloadStringAsync(_Client.Location);
+ {
+ // GET
+ Logger.Log.Debug("[CapsClient] GET " + _Address);
+ _Request = CapsBase.DownloadStringAsync(_Address, _ClientCert, millisecondsTimeout, DownloadProgressHandler,
+ RequestCompletedHandler);
+ }
else
- _Client.UploadDataAsync(_Client.Location, postData);
+ {
+ // POST
+ Logger.Log.Debug("[CapsClient] POST (" + postData.Length + " bytes) " + _Address);
+ _Request = CapsBase.UploadDataAsync(_Address, _ClientCert, contentType, postData, millisecondsTimeout, null,
+ DownloadProgressHandler, RequestCompletedHandler);
+ }
}
public OSD GetResponse(int millisecondsTimeout)
{
- OSD response = null;
- AutoResetEvent waitEvent = new AutoResetEvent(false);
- OnComplete += delegate(CapsClient client, OSD result, Exception error) { response = result; waitEvent.Set(); };
- BeginGetResponse();
- waitEvent.WaitOne(millisecondsTimeout, false);
- return response;
+ BeginGetResponse(millisecondsTimeout);
+ _ResponseEvent.WaitOne(millisecondsTimeout, false);
+ return _Response;
}
- public OSD GetResponse(OSD data, int millisecondsTimeout)
+ public OSD GetResponse(OSD data, OSDFormat format, int millisecondsTimeout)
{
- OSD response = null;
- AutoResetEvent waitEvent = new AutoResetEvent(false);
- OnComplete += delegate(CapsClient client, OSD result, Exception error) { response = result; waitEvent.Set(); };
- BeginGetResponse(data);
- waitEvent.WaitOne(millisecondsTimeout, false);
- return response;
- }
-
- public OSD GetResponse(OSD data, bool json, int millisecondsTimeout)
- {
- OSD response = null;
- AutoResetEvent waitEvent = new AutoResetEvent(false);
- OnComplete += delegate(CapsClient client, OSD result, Exception error) { response = result; waitEvent.Set(); };
- BeginGetResponse(data, json);
- waitEvent.WaitOne(millisecondsTimeout, false);
- return response;
- }
-
- public OSD GetResponse(byte[] postData, int millisecondsTimeout)
- {
- OSD response = null;
- AutoResetEvent waitEvent = new AutoResetEvent(false);
- OnComplete += delegate(CapsClient client, OSD result, Exception error) { response = result; waitEvent.Set(); };
- BeginGetResponse(postData);
- waitEvent.WaitOne(millisecondsTimeout, false);
- return response;
+ BeginGetResponse(data, format, millisecondsTimeout);
+ _ResponseEvent.WaitOne(millisecondsTimeout, false);
+ return _Response;
}
public OSD GetResponse(byte[] postData, string contentType, int millisecondsTimeout)
{
- OSD response = null;
- AutoResetEvent waitEvent = new AutoResetEvent(false);
- OnComplete += delegate(CapsClient client, OSD result, Exception error) { response = result; waitEvent.Set(); };
- BeginGetResponse(postData, contentType);
- waitEvent.WaitOne(millisecondsTimeout, false);
- return response;
+ BeginGetResponse(postData, contentType, millisecondsTimeout);
+ _ResponseEvent.WaitOne(millisecondsTimeout, false);
+ return _Response;
}
public void Cancel()
{
- if (_Client.IsBusy)
- _Client.CancelAsync();
+ if (_Request != null)
+ _Request.Abort();
}
- #region Callback Handlers
-
- private void Client_DownloadProgressChanged(object sender, CapsBase.DownloadProgressChangedEventArgs e)
+ void DownloadProgressHandler(HttpWebRequest request, HttpWebResponse response, int bytesReceived, int totalBytesToReceive)
{
- if (OnProgress != null)
+ _Request = request;
+
+ if (OnDownloadProgress != null)
{
- try { OnProgress(this, e.BytesReceived, 0, e.TotalBytesToReceive, 0); }
+ try { OnDownloadProgress(this, bytesReceived, totalBytesToReceive); }
catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
}
}
- private void Client_UploadProgressChanged(object sender, CapsBase.UploadProgressChangedEventArgs e)
+ void RequestCompletedHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error)
{
- if (OnProgress != null)
+ _Request = request;
+
+ OSD result = null;
+
+ if (responseData != null)
{
- try { OnProgress(this, e.BytesReceived, e.BytesSent, e.TotalBytesToReceive, e.TotalBytesToSend); }
+ try { result = OSDParser.Deserialize(responseData); }
+ catch (Exception ex) { error = ex; }
+ }
+
+ FireCompleteCallback(result, error);
+ }
+
+ private void FireCompleteCallback(OSD result, Exception error)
+ {
+ CompleteCallback callback = OnComplete;
+ if (callback != null)
+ {
+ try { callback(this, result, error); }
catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
}
+
+ _Response = result;
+ _ResponseEvent.Set();
}
-
- private void Client_UploadDataCompleted(object sender, CapsBase.UploadDataCompletedEventArgs e)
- {
- if (OnComplete != null && !e.Cancelled)
- {
- if (e.Error == null)
- {
- OSD result = OSDParser.Deserialize(e.Result);
-
- try { OnComplete(this, result, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- else
- {
- // Some error occurred, try to figure out what happened
- HttpStatusCode code = HttpStatusCode.OK;
- if (e.Error is WebException && ((WebException)e.Error).Response != null)
- code = ((HttpWebResponse)((WebException)e.Error).Response).StatusCode;
-
- if (code == HttpStatusCode.BadGateway)
- {
- // 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
- BeginGetResponse(_PostData, _ContentType);
- }
- else if (code != HttpStatusCode.OK)
- {
- // Status code was set to something unknown, this is a failure
- Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, code);
-
- try { OnComplete(this, null, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- else
- {
- // Status code was not set, some other error occurred. This is a failure
- Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, e.Error.Message);
-
- try { OnComplete(this, null, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- }
- }
- else if (e.Cancelled)
- {
- Logger.Log.Debug("Capability action at " + _Client.Location + " cancelled");
- }
- }
-
- private void Client_DownloadStringCompleted(object sender, CapsBase.DownloadStringCompletedEventArgs e)
- {
- if (OnComplete != null && !e.Cancelled)
- {
- if (e.Error == null)
- {
- OSD result = OSDParser.DeserializeLLSDXml(e.Result);
-
- try { OnComplete(this, result, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- else
- {
- // Some error occurred, try to figure out what happened
- HttpStatusCode code = HttpStatusCode.OK;
- if (e.Error is WebException && ((WebException)e.Error).Response != null)
- code = ((HttpWebResponse)((WebException)e.Error).Response).StatusCode;
-
- if (code == HttpStatusCode.BadGateway)
- {
- // 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
- BeginGetResponse(_PostData, _ContentType);
- }
- else if (code != HttpStatusCode.OK)
- {
- // Status code was set to something unknown, this is a failure
- Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, code);
-
- try { OnComplete(this, null, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- else
- {
- // Status code was not set, some other error occurred. This is a failure
- Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, e.Error.Message);
-
- try { OnComplete(this, null, e.Error); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
- }
- }
- else if (e.Cancelled)
- {
- Logger.Log.Debug("Capability action at " + _Client.Location + " cancelled");
- }
- }
-
- #endregion Callback Handlers
}
}
diff --git a/OpenMetaverse.Http/EventQueueClient.cs b/OpenMetaverse.Http/EventQueueClient.cs
index 04adf36c..4deda398 100644
--- a/OpenMetaverse.Http/EventQueueClient.cs
+++ b/OpenMetaverse.Http/EventQueueClient.cs
@@ -33,41 +33,39 @@ namespace OpenMetaverse.Http
{
public class EventQueueClient
{
- ///
- ///
- ///
+ /// =
+ public const int REQUEST_TIMEOUT = 1000 * 120;
+
public delegate void ConnectedCallback();
- ///
- ///
- ///
- ///
- ///
public delegate void EventCallback(string eventName, OSDMap body);
- ///
public ConnectedCallback OnConnected;
- ///
public EventCallback OnEvent;
- public IWebProxy Proxy;
+ public bool Running { get { return _Running; } }
- public bool Running { get { return _Running && _Client.IsBusy; } }
-
- protected CapsBase _Client;
+ protected Uri _Address;
protected bool _Dead;
protected bool _Running;
+ protected HttpWebRequest _Request;
public EventQueueClient(Uri eventQueueLocation)
{
- _Client = new CapsBase(eventQueueLocation, null);
- _Client.OpenWriteCompleted += new CapsBase.OpenWriteCompletedEventHandler(Client_OpenWriteCompleted);
- _Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted);
+ _Address = eventQueueLocation;
}
public void Start()
{
_Dead = false;
- _Client.OpenWriteAsync(_Client.Location);
+
+ // Create an EventQueueGet request
+ OSDMap request = new OSDMap();
+ request["ack"] = new OSD();
+ request["done"] = OSD.FromBoolean(false);
+
+ byte[] postData = OSDParser.SerializeLLSDXmlBytes(request);
+
+ _Request = CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT, OpenWriteHandler, null, RequestCompletedHandler);
}
public void Stop(bool immediate)
@@ -77,60 +75,71 @@ namespace OpenMetaverse.Http
if (immediate)
_Running = false;
- if (_Client.IsBusy)
- _Client.CancelAsync();
+ if (_Request != null)
+ _Request.Abort();
}
- #region Callback Handlers
-
- private void Client_OpenWriteCompleted(object sender, CapsBase.OpenWriteCompletedEventArgs e)
+ void OpenWriteHandler(HttpWebRequest request)
{
- bool raiseEvent = false;
+ _Running = true;
+ _Request = request;
- if (!_Dead)
+ Logger.Log.Debug("Capabilities event queue connected");
+
+ // The event queue is starting up for the first time
+ if (OnConnected != null)
{
- if (!_Running) raiseEvent = true;
-
- // We are connected to the event queue
- _Running = true;
- }
-
- // Create an EventQueueGet request
- OSDMap request = new OSDMap();
- request["ack"] = new OSD();
- request["done"] = OSD.FromBoolean(false);
-
- byte[] postData = OSDParser.SerializeLLSDXmlBytes(request);
-
- _Client.UploadDataAsync(_Client.Location, postData);
-
- if (raiseEvent)
- {
- Logger.Log.Debug("Capabilities event queue connected");
-
- // The event queue is starting up for the first time
- if (OnConnected != null)
- {
- try { OnConnected(); }
- catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
- }
+ try { OnConnected(); }
+ catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
}
}
- private void Client_UploadDataCompleted(object sender, CapsBase.UploadDataCompletedEventArgs e)
+ void RequestCompletedHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error)
{
+ // We don't care about this request now that it has completed
+ _Request = null;
+
OSDArray events = null;
int ack = 0;
- if (e.Error != null)
+ if (responseData != null)
{
+ // Got a response
+ OSDMap result = OSDParser.DeserializeLLSDXml(responseData) as OSDMap;
+
+ if (result != null)
+ {
+ events = result["events"] as OSDArray;
+ ack = result["id"].AsInteger();
+ }
+ else
+ {
+ Logger.Log.Warn("Got an unparseable response from the event queue: \"" +
+ System.Text.Encoding.UTF8.GetString(responseData) + "\"");
+ }
+ }
+ else if (error != null)
+ {
+ #region Error handling
+
HttpStatusCode code = HttpStatusCode.OK;
- if (e.Error is WebException && ((WebException)e.Error).Response != null)
- code = ((HttpWebResponse)((WebException)e.Error).Response).StatusCode;
+
+ if (error is WebException)
+ {
+ WebException webException = (WebException)error;
+
+ if (webException.Response != null)
+ code = ((HttpWebResponse)webException.Response).StatusCode;
+ else if (webException.Status == WebExceptionStatus.RequestCanceled)
+ goto HandlingDone;
+ }
+
+ if (error is WebException && ((WebException)error).Response != null)
+ code = ((HttpWebResponse)((WebException)error).Response).StatusCode;
if (code == HttpStatusCode.NotFound || code == HttpStatusCode.Gone)
{
- Logger.Log.InfoFormat("Closing event queue at {0} due to missing caps URI", _Client.Location);
+ Logger.Log.InfoFormat("Closing event queue at {0} due to missing caps URI", _Address);
_Running = false;
_Dead = true;
@@ -143,55 +152,50 @@ namespace OpenMetaverse.Http
// interprets this as a generic error and returns a 502 to us
// that we ignore
}
- else if (!e.Cancelled)
+ else
{
// Try to log a meaningful error message
if (code != HttpStatusCode.OK)
{
Logger.Log.WarnFormat("Unrecognized caps connection problem from {0}: {1}",
- _Client.Location, code);
+ _Address, code);
}
- else if (e.Error.InnerException != null)
+ else if (error.InnerException != null)
{
Logger.Log.WarnFormat("Unrecognized caps exception from {0}: {1}",
- _Client.Location, e.Error.InnerException.Message);
+ _Address, error.InnerException.Message);
}
else
{
Logger.Log.WarnFormat("Unrecognized caps exception from {0}: {1}",
- _Client.Location, e.Error.Message);
+ _Address, error.Message);
}
}
- }
- else if (!e.Cancelled && e.Result != null)
- {
- // Got a response
- OSD result = OSDParser.DeserializeLLSDXml(e.Result);
- if (result != null && result.Type == OSDType.Map)
- {
- // Parse any events returned by the event queue
- OSDMap map = (OSDMap)result;
- events = (OSDArray)map["events"];
- ack = map["id"].AsInteger();
- }
+ #endregion Error handling
}
- else if (e.Cancelled)
+ else
{
- // Connection was cancelled
- Logger.Log.Debug("Cancelled connection to event queue at " + _Client.Location);
+ Logger.Log.Warn("No response from the event queue but no reported error either");
}
+ HandlingDone:
+
+ #region Resume the connection
+
if (_Running)
{
- OSDMap request = new OSDMap();
- if (ack != 0) request["ack"] = OSD.FromInteger(ack);
- else request["ack"] = new OSD();
- request["done"] = OSD.FromBoolean(_Dead);
+ OSDMap osdRequest = new OSDMap();
+ if (ack != 0) osdRequest["ack"] = OSD.FromInteger(ack);
+ else osdRequest["ack"] = new OSD();
+ osdRequest["done"] = OSD.FromBoolean(_Dead);
- byte[] postData = OSDParser.SerializeLLSDXmlBytes(request);
+ byte[] postData = OSDParser.SerializeLLSDXmlBytes(osdRequest);
- _Client.UploadDataAsync(_Client.Location, postData);
+ // Resume the connection. The event handler for the connection opening
+ // just sets class _Request variable to the current HttpWebRequest
+ CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT,
+ delegate(HttpWebRequest newRequest) { _Request = newRequest; }, null, RequestCompletedHandler);
// If the event queue is dead at this point, turn it off since
// that was the last thing we want to do
@@ -202,6 +206,10 @@ namespace OpenMetaverse.Http
}
}
+ #endregion Resume the connection
+
+ #region Handle incoming events
+
if (OnEvent != null && events != null && events.Count > 0)
{
// Fire callbacks for each event received
@@ -214,8 +222,8 @@ namespace OpenMetaverse.Http
catch (Exception ex) { Logger.Log.Error(ex.Message, ex); }
}
}
- }
- #endregion Callback Handlers
+ #endregion Handle incoming events
+ }
}
}
diff --git a/OpenMetaverse.StructuredData/StructuredData.cs b/OpenMetaverse.StructuredData/StructuredData.cs
index 2f58ce71..d9b7b21a 100644
--- a/OpenMetaverse.StructuredData/StructuredData.cs
+++ b/OpenMetaverse.StructuredData/StructuredData.cs
@@ -62,6 +62,13 @@ namespace OpenMetaverse.StructuredData
Array
}
+ public enum OSDFormat
+ {
+ Xml = 0,
+ Json,
+ Binary
+ }
+
///
///
///
diff --git a/OpenMetaverse.Utilities/RegistrationApi.cs b/OpenMetaverse.Utilities/RegistrationApi.cs
index 0e61272e..06d86249 100644
--- a/OpenMetaverse.Utilities/RegistrationApi.cs
+++ b/OpenMetaverse.Utilities/RegistrationApi.cs
@@ -35,6 +35,8 @@ namespace OpenMetaverse
{
public class RegistrationApi
{
+ const int REQUEST_TIMEOUT = 1000 * 100;
+
private struct UserInfo
{
public string FirstName;
@@ -137,7 +139,7 @@ namespace OpenMetaverse
CapsClient request = new CapsClient(RegistrationApiCaps);
request.OnComplete += new CapsClient.CompleteCallback(GatherCapsResponse);
- request.BeginGetResponse(postData);
+ request.BeginGetResponse(postData, "application/x-www-form-urlencoded", REQUEST_TIMEOUT);
}
private void GatherCapsResponse(CapsClient client, OSD response, Exception error)
@@ -168,7 +170,7 @@ namespace OpenMetaverse
CapsClient request = new CapsClient(_caps.GetErrorCodes);
request.OnComplete += new CapsClient.CompleteCallback(GatherErrorMessagesResponse);
- request.BeginGetResponse();
+ request.BeginGetResponse(REQUEST_TIMEOUT);
}
private void GatherErrorMessagesResponse(CapsClient client, OSD response, Exception error)
@@ -206,7 +208,7 @@ namespace OpenMetaverse
CapsClient request = new CapsClient(_caps.GetLastNames);
request.OnComplete += new CapsClient.CompleteCallback(GatherLastNamesResponse);
- request.BeginGetResponse();
+ request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
}
@@ -250,7 +252,7 @@ namespace OpenMetaverse
CapsClient request = new CapsClient(_caps.CheckName);
request.OnComplete += new CapsClient.CompleteCallback(CheckNameResponse);
- request.BeginGetResponse();
+ request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME:
return false;
@@ -316,7 +318,7 @@ namespace OpenMetaverse
// Make the request
CapsClient request = new CapsClient(_caps.CreateUser);
request.OnComplete += new CapsClient.CompleteCallback(CreateUserResponse);
- request.BeginGetResponse();
+ request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
return UUID.Zero;
diff --git a/OpenMetaverse.Utilities/VoiceManager.cs b/OpenMetaverse.Utilities/VoiceManager.cs
index 4e3c1f1f..ac6f772c 100644
--- a/OpenMetaverse.Utilities/VoiceManager.cs
+++ b/OpenMetaverse.Utilities/VoiceManager.cs
@@ -335,7 +335,7 @@ namespace OpenMetaverse.Utilities
CapsClient request = new CapsClient(url);
OSDMap body = new OSDMap();
request.OnComplete += new CapsClient.CompleteCallback(callback);
- request.BeginGetResponse(body);
+ request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
return true;
}
diff --git a/OpenMetaverse/AgentManager.cs b/OpenMetaverse/AgentManager.cs
index 11b5aac3..748a616c 100644
--- a/OpenMetaverse/AgentManager.cs
+++ b/OpenMetaverse/AgentManager.cs
@@ -1490,10 +1490,8 @@ namespace OpenMetaverse
ChatSessionAcceptInvitation acceptInvite = new ChatSessionAcceptInvitation();
acceptInvite.SessionID = session_id;
- byte[] postData = OSDParser.SerializeLLSDXmlBytes(acceptInvite.Serialize());
-
CapsClient request = new CapsClient(url);
- request.BeginGetResponse(postData);
+ request.BeginGetResponse(acceptInvite.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
}
else
{
@@ -1527,7 +1525,7 @@ namespace OpenMetaverse
byte[] postData = StructuredData.OSDParser.SerializeLLSDXmlBytes(startConference.Serialize());
CapsClient request = new CapsClient(url);
- request.BeginGetResponse(postData);
+ request.BeginGetResponse(startConference.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
}
else
{
@@ -3353,12 +3351,8 @@ namespace OpenMetaverse
req.SessionID = sessionID;
req.AgentID = memberID;
- OSDMap map = req.Serialize();
-
- byte[] postData = OSDParser.SerializeLLSDXmlBytes(map);
-
CapsClient request = new CapsClient(url);
- request.BeginGetResponse(postData);
+ request.BeginGetResponse(req.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
}
else
{
diff --git a/OpenMetaverse/Caps.cs b/OpenMetaverse/Caps.cs
index 19c1fadc..0127bf9e 100644
--- a/OpenMetaverse/Caps.cs
+++ b/OpenMetaverse/Caps.cs
@@ -51,7 +51,6 @@ namespace OpenMetaverse
/// The simulator that generated the event
//public delegate void EventQueueCallback(string message, StructuredData.OSD body, Simulator simulator);
-
public delegate void EventQueueCallback(string capsKey, IMessage message, Simulator simulator);
/// Reference to the simulator this system is connected to
@@ -79,7 +78,6 @@ namespace OpenMetaverse
}
}
-
///
/// Default constructor
///
@@ -171,7 +169,7 @@ namespace OpenMetaverse
_SeedRequest = new CapsClient(new Uri(_SeedCapsURI));
_SeedRequest.OnComplete += new CapsClient.CompleteCallback(SeedRequestCompleteHandler);
- _SeedRequest.BeginGetResponse(req);
+ _SeedRequest.BeginGetResponse(req, OSDFormat.Xml, Simulator.Client.Settings.CAPS_TIMEOUT);
}
private void SeedRequestCompleteHandler(CapsClient client, OSD result, Exception error)
diff --git a/OpenMetaverse/GridManager.cs b/OpenMetaverse/GridManager.cs
index 12728cfb..9f2c5bed 100644
--- a/OpenMetaverse/GridManager.cs
+++ b/OpenMetaverse/GridManager.cs
@@ -286,7 +286,7 @@ namespace OpenMetaverse
CapsClient request = new CapsClient(url);
request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler);
- request.BeginGetResponse(body);
+ request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
}
}
diff --git a/OpenMetaverse/InventoryManager.cs b/OpenMetaverse/InventoryManager.cs
index b1041f14..894edf4d 100644
--- a/OpenMetaverse/InventoryManager.cs
+++ b/OpenMetaverse/InventoryManager.cs
@@ -1834,7 +1834,7 @@ namespace OpenMetaverse
}
public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType,
- InventoryType invType, UUID folderID, CapsClient.ProgressCallback progCallback, ItemCreatedFromAssetCallback callback)
+ InventoryType invType, UUID folderID, ItemCreatedFromAssetCallback callback)
{
if (_Client.Network.CurrentSim == null || _Client.Network.CurrentSim.Caps == null)
throw new Exception("NewFileAgentInventory capability is not currently available");
@@ -1853,9 +1853,9 @@ namespace OpenMetaverse
// Make the request
CapsClient request = new CapsClient(url);
request.OnComplete += new CapsClient.CompleteCallback(CreateItemFromAssetResponse);
- request.UserData = new object[] { progCallback, callback, data };
+ request.UserData = new object[] { callback, data, _Client.Settings.CAPS_TIMEOUT };
- request.BeginGetResponse(query);
+ request.BeginGetResponse(query, OSDFormat.Xml, _Client.Settings.CAPS_TIMEOUT);
}
else
{
@@ -2056,13 +2056,11 @@ namespace OpenMetaverse
OSDMap query = new OSDMap();
query.Add("item_id", OSD.FromUUID(notecardID));
- byte[] postData = StructuredData.OSDParser.SerializeLLSDXmlBytes(query);
-
// Make the request
CapsClient request = new CapsClient(url);
request.OnComplete += new CapsClient.CompleteCallback(UploadNotecardAssetResponse);
- request.UserData = new object[2] { new KeyValuePair(callback, data), notecardID };
- request.BeginGetResponse(postData);
+ request.UserData = new object[] { new KeyValuePair(callback, data), notecardID };
+ request.BeginGetResponse(query, OSDFormat.Xml, _Client.Settings.CAPS_TIMEOUT);
}
else
{
@@ -2987,9 +2985,9 @@ namespace OpenMetaverse
private void CreateItemFromAssetResponse(CapsClient client, OSD result, Exception error)
{
object[] args = (object[])client.UserData;
- CapsClient.ProgressCallback progCallback = (CapsClient.ProgressCallback)args[0];
- ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[1];
- byte[] itemData = (byte[])args[2];
+ ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[0];
+ byte[] itemData = (byte[])args[1];
+ int millisecondsTimeout = (int)args[2];
if (result == null)
{
@@ -3011,10 +3009,9 @@ namespace OpenMetaverse
// This makes the assumption that all uploads go to CurrentSim, to avoid
// the problem of HttpRequestState not knowing anything about simulators
CapsClient upload = new CapsClient(new Uri(uploadURL));
- upload.OnProgress += progCallback;
upload.OnComplete += new CapsClient.CompleteCallback(CreateItemFromAssetResponse);
upload.UserData = new object[] { null, callback, itemData };
- upload.BeginGetResponse(itemData, "application/octet-stream");
+ upload.BeginGetResponse(itemData, "application/octet-stream", millisecondsTimeout);
}
else if (status == "complete")
{
@@ -3609,7 +3606,7 @@ namespace OpenMetaverse
CapsClient upload = new CapsClient(new Uri(uploadURL));
upload.OnComplete += new CapsClient.CompleteCallback(UploadNotecardAssetResponse);
upload.UserData = new object[2] { kvp, (UUID)(((object[])client.UserData)[1]) };
- upload.BeginGetResponse(itemData, "application/octet-stream");
+ upload.BeginGetResponse(itemData, "application/octet-stream", _Client.Settings.CAPS_TIMEOUT);
}
else if (status == "complete")
{
diff --git a/OpenMetaverse/Login.cs b/OpenMetaverse/Login.cs
index 0db509c9..20809c74 100644
--- a/OpenMetaverse/Login.cs
+++ b/OpenMetaverse/Login.cs
@@ -1350,7 +1350,7 @@ namespace OpenMetaverse
loginRequest.OnComplete += new CapsClient.CompleteCallback(LoginReplyLLSDHandler);
loginRequest.UserData = CurrentContext;
UpdateLoginStatus(LoginStatus.ConnectingToLogin, String.Format("Logging in as {0} {1}...", loginParams.FirstName, loginParams.LastName));
- loginRequest.BeginGetResponse(OSDParser.SerializeLLSDXmlBytes(loginLLSD), "application/xml+llsd");
+ loginRequest.BeginGetResponse(loginLLSD, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
#endregion
}
diff --git a/OpenMetaverse/Messages/CableBeachHelpers.cs b/OpenMetaverse/Messages/CableBeachHelpers.cs
index d22b66ca..50cfb3c8 100644
--- a/OpenMetaverse/Messages/CableBeachHelpers.cs
+++ b/OpenMetaverse/Messages/CableBeachHelpers.cs
@@ -9,100 +9,149 @@ namespace OpenMetaverse.Messages.CableBeach
#region SL / file extension / content-type conversions
- public static string SLAssetTypeToContentType(int assetType)
+ public static string SLAssetTypeToContentType(AssetType assetType)
{
switch (assetType)
{
- case 0:
+ case AssetType.Texture:
return "image/x-j2c";
- case 1:
+ case AssetType.Sound:
return "application/ogg";
- case 2:
+ case AssetType.CallingCard:
return "application/vnd.ll.callingcard";
- case 3:
+ case AssetType.Landmark:
return "application/vnd.ll.landmark";
- case 5:
+ case AssetType.Clothing:
return "application/vnd.ll.clothing";
- case 6:
+ case AssetType.Object:
return "application/vnd.ll.primitive";
- case 7:
+ case AssetType.Notecard:
return "application/vnd.ll.notecard";
- case 10:
+ case AssetType.Folder:
+ return "application/vnd.ll.folder";
+ case AssetType.RootFolder:
+ return "application/vnd.ll.rootfolder";
+ case AssetType.LSLText:
return "application/vnd.ll.lsltext";
- case 11:
+ case AssetType.LSLBytecode:
return "application/vnd.ll.lslbyte";
- case 13:
+ case AssetType.TextureTGA:
+ case AssetType.ImageTGA:
+ return "image/tga";
+ case AssetType.Bodypart:
return "application/vnd.ll.bodypart";
- case 20:
+ case AssetType.TrashFolder:
+ return "application/vnd.ll.trashfolder";
+ case AssetType.SnapshotFolder:
+ return "application/vnd.ll.snapshotfolder";
+ case AssetType.LostAndFoundFolder:
+ return "application/vnd.ll.lostandfoundfolder";
+ case AssetType.SoundWAV:
+ return "audio/x-wav";
+ case AssetType.ImageJPEG:
+ return "image/jpeg";
+ case AssetType.Animation:
return "application/vnd.ll.animation";
- case 21:
+ case AssetType.Gesture:
return "application/vnd.ll.gesture";
+ case AssetType.Simstate:
+ case AssetType.Unknown:
default:
return "application/octet-stream";
}
}
- public static int ContentTypeToSLAssetType(string contentType)
+ public static AssetType ContentTypeToSLAssetType(string contentType)
{
switch (contentType)
{
case "image/x-j2c":
- return 0;
+ return AssetType.Texture;
case "application/ogg":
- return 1;
+ return AssetType.Sound;
case "application/vnd.ll.callingcard":
- return 2;
+ return AssetType.CallingCard;
case "application/vnd.ll.landmark":
- return 3;
+ return AssetType.Landmark;
case "application/vnd.ll.clothing":
- return 5;
+ return AssetType.Clothing;
case "application/vnd.ll.primitive":
- return 6;
+ return AssetType.Object;
case "application/vnd.ll.notecard":
- return 7;
+ return AssetType.Notecard;
+ case "application/vnd.ll.folder":
+ return AssetType.Folder;
+ case "application/vnd.ll.rootfolder":
+ return AssetType.RootFolder;
case "application/vnd.ll.lsltext":
- return 10;
+ return AssetType.LSLText;
case "application/vnd.ll.lslbyte":
- return 11;
+ return AssetType.LSLBytecode;
+ case "image/tga":
+ // Note that AssetType.TextureTGA will be converted to AssetType.ImageTGA
+ return AssetType.ImageTGA;
case "application/vnd.ll.bodypart":
- return 13;
+ return AssetType.Bodypart;
+ case "application/vnd.ll.trashfolder":
+ return AssetType.TrashFolder;
+ case "application/vnd.ll.snapshotfolder":
+ return AssetType.SnapshotFolder;
+ case "application/vnd.ll.lostandfoundfolder":
+ return AssetType.LostAndFoundFolder;
+ case "audio/x-wav":
+ return AssetType.SoundWAV;
+ case "image/jpeg":
+ return AssetType.ImageJPEG;
case "application/vnd.ll.animation":
- return 20;
+ return AssetType.Animation;
case "application/vnd.ll.gesture":
- return 21;
+ return AssetType.Gesture;
+ case "application/octet-stream":
default:
- return -1;
+ return AssetType.Unknown;
}
}
- public static int ContentTypeToSLInvType(string contentType)
+ public static InventoryType ContentTypeToSLInvType(string contentType)
{
switch (contentType)
{
case "image/x-j2c":
- return (int)InventoryType.Texture;
+ case "image/tga":
+ case "image/jpeg":
+ return InventoryType.Texture;
case "application/ogg":
- return (int)InventoryType.Sound;
+ case "audio/x-wav":
+ return InventoryType.Sound;
case "application/vnd.ll.callingcard":
- return (int)InventoryType.CallingCard;
+ return InventoryType.CallingCard;
case "application/vnd.ll.landmark":
- return (int)InventoryType.Landmark;
+ return InventoryType.Landmark;
case "application/vnd.ll.clothing":
case "application/vnd.ll.bodypart":
- return (int)InventoryType.Wearable;
+ return InventoryType.Wearable;
case "application/vnd.ll.primitive":
- return (int)InventoryType.Object;
+ return InventoryType.Object;
case "application/vnd.ll.notecard":
- return (int)InventoryType.Notecard;
+ return InventoryType.Notecard;
+ case "application/vnd.ll.folder":
+ return InventoryType.Folder;
+ case "application/vnd.ll.rootfolder":
+ return InventoryType.RootCategory;
case "application/vnd.ll.lsltext":
case "application/vnd.ll.lslbyte":
- return (int)InventoryType.LSL;
+ return InventoryType.LSL;
+ case "application/vnd.ll.trashfolder":
+ case "application/vnd.ll.snapshotfolder":
+ case "application/vnd.ll.lostandfoundfolder":
+ return InventoryType.Folder;
case "application/vnd.ll.animation":
- return (int)InventoryType.Animation;
+ return InventoryType.Animation;
case "application/vnd.ll.gesture":
- return (int)InventoryType.Gesture;
+ return InventoryType.Gesture;
+ case "application/octet-stream":
default:
- return (int)InventoryType.Unknown;
+ return InventoryType.Unknown;
}
}
@@ -124,16 +173,33 @@ namespace OpenMetaverse.Messages.CableBeach
return "primitive";
case "application/vnd.ll.notecard":
return "notecard";
+ case "application/vnd.ll.folder":
+ return "folder";
+ case "application/vnd.ll.rootfolder":
+ return "rootfolder";
case "application/vnd.ll.lsltext":
return "lsltext";
case "application/vnd.ll.lslbyte":
return "lslbyte";
+ case "image/tga":
+ return "tga";
case "application/vnd.ll.bodypart":
return "bodypart";
+ case "application/vnd.ll.trashfolder":
+ return "trashfolder";
+ case "application/vnd.ll.snapshotfolder":
+ return "snapshotfolder";
+ case "application/vnd.ll.lostandfoundfolder":
+ return "lostandfoundfolder";
+ case "audio/x-wav":
+ return "wav";
+ case "image/jpeg":
+ return "jpg";
case "application/vnd.ll.animation":
return "animatn";
case "application/vnd.ll.gesture":
return "gesture";
+ case "application/octet-stream":
default:
return "binary";
}
@@ -144,10 +210,7 @@ namespace OpenMetaverse.Messages.CableBeach
switch (extension)
{
case "texture":
- case "jp2":
- case "j2c":
return "image/x-j2c";
- case "sound":
case "ogg":
return "application/ogg";
case "callingcard":
@@ -160,16 +223,33 @@ namespace OpenMetaverse.Messages.CableBeach
return "application/vnd.ll.primitive";
case "notecard":
return "application/vnd.ll.notecard";
- case "lsl":
+ case "folder":
+ return "application/vnd.ll.folder";
+ case "rootfolder":
+ return "application/vnd.ll.rootfolder";
+ case "lsltext":
return "application/vnd.ll.lsltext";
- case "lso":
+ case "lslbyte":
return "application/vnd.ll.lslbyte";
+ case "tga":
+ return "image/tga";
case "bodypart":
return "application/vnd.ll.bodypart";
+ case "trashfolder":
+ return "application/vnd.ll.trashfolder";
+ case "snapshotfolder":
+ return "application/vnd.ll.snapshotfolder";
+ case "lostandfoundfolder":
+ return "application/vnd.ll.lostandfoundfolder";
+ case "wav":
+ return "audio/x-wav";
+ case "jpg":
+ return "image/jpeg";
case "animatn":
return "application/vnd.ll.animation";
case "gesture":
return "application/vnd.ll.gesture";
+ case "binary":
default:
return "application/octet-stream";
}
diff --git a/OpenMetaverse/Messages/CableBeachMessages.cs b/OpenMetaverse/Messages/CableBeachMessages.cs
index 1f3c6253..ee824b8a 100644
--- a/OpenMetaverse/Messages/CableBeachMessages.cs
+++ b/OpenMetaverse/Messages/CableBeachMessages.cs
@@ -691,12 +691,15 @@ namespace OpenMetaverse.Messages.CableBeach
{
public bool Success;
public string Message;
+ public InventoryBlock Object;
public OSDMap Serialize()
{
OSDMap map = new OSDMap();
map["success"] = OSD.FromBoolean(Success);
map["message"] = OSD.FromString(Message);
+ if (Object != null)
+ map["object"] = Object.Serialize();
return map;
}
@@ -704,6 +707,20 @@ namespace OpenMetaverse.Messages.CableBeach
{
Success = map["success"].AsBoolean();
Message = map["message"].AsString();
+ OSDMap objMap = map["object"] as OSDMap;
+ if (objMap != null)
+ {
+ if (objMap.ContainsKey("asset_id"))
+ Object = new InventoryBlockItem();
+ else
+ Object = new InventoryBlockFolder();
+
+ Object.Deserialize(objMap);
+ }
+ else
+ {
+ Object = null;
+ }
}
}
diff --git a/OpenMetaverse/ParcelManager.cs b/OpenMetaverse/ParcelManager.cs
index 8e686fd3..da3e1233 100644
--- a/OpenMetaverse/ParcelManager.cs
+++ b/OpenMetaverse/ParcelManager.cs
@@ -674,15 +674,12 @@ namespace OpenMetaverse
//body["user_look_at"] = ulat;
OSDMap body = req.Serialize();
- byte[] postData = StructuredData.OSDParser.SerializeLLSDXmlBytes(body);
CapsClient capsPost = new CapsClient(url);
- capsPost.BeginGetResponse(postData);
-
+ capsPost.BeginGetResponse(body, OSDFormat.Xml, simulator.Client.Settings.CAPS_TIMEOUT);
}
else
{
-
ParcelPropertiesUpdatePacket request = new ParcelPropertiesUpdatePacket();
request.AgentData.AgentID = simulator.Client.Self.AgentID;
diff --git a/OpenMetaverse/Settings.cs b/OpenMetaverse/Settings.cs
index 9caf5307..ebb64b60 100644
--- a/OpenMetaverse/Settings.cs
+++ b/OpenMetaverse/Settings.cs
@@ -85,10 +85,9 @@ namespace OpenMetaverse
/// time out
public int LOGOUT_TIMEOUT = 5 * 1000;
- /// Number of milliseconds before a CAPS call will time out
- /// and try again
- /// Setting this too low will cause web requests to repeatedly
- /// time out and retry
+ /// Number of milliseconds before a CAPS call will time out
+ /// Setting this too low will cause web requests time out and
+ /// possibly retry repeatedly
public int CAPS_TIMEOUT = 60 * 1000;
/// Number of milliseconds for xml-rpc to timeout
diff --git a/Programs/GridImageUpload/frmGridImageUpload.cs b/Programs/GridImageUpload/frmGridImageUpload.cs
index da59ef8a..75bf3ee8 100644
--- a/Programs/GridImageUpload/frmGridImageUpload.cs
+++ b/Programs/GridImageUpload/frmGridImageUpload.cs
@@ -304,16 +304,6 @@ namespace GridImageUpload
Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with SL Image Upload", AssetType.Texture,
InventoryType.Texture, Client.Inventory.FindFolderForType(AssetType.Texture),
-
- delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend)
- {
- if (bytesSent > 0)
- {
- Transferred = (int)bytesSent;
- BeginInvoke((MethodInvoker)delegate() { SetProgress(); });
- }
- },
-
delegate(bool success, string status, UUID itemID, UUID assetID)
{
if (this.InvokeRequired)
diff --git a/Programs/GridProxy/GridProxy.cs b/Programs/GridProxy/GridProxy.cs
index a38d81ba..ff569b29 100644
--- a/Programs/GridProxy/GridProxy.cs
+++ b/Programs/GridProxy/GridProxy.cs
@@ -1132,8 +1132,8 @@ namespace GridProxy
remoteComplete.Set();
}
);
- loginRequest.BeginGetResponse(content, "application/xml+llsd"); //xml+llsd
- remoteComplete.WaitOne(30000, false);
+ loginRequest.BeginGetResponse(content, "application/llsd+xml", 1000 * 100);
+ remoteComplete.WaitOne(1000 * 100, false);
if (response == null) {
byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n");
diff --git a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs
index 015d6e28..5322bcae 100644
--- a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs
+++ b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs
@@ -64,13 +64,6 @@ namespace OpenMetaverse.TestClient
Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with TestClient",
AssetType.Texture, InventoryType.Texture, Client.Inventory.FindFolderForType(AssetType.Texture),
-
- delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend)
- {
- if (bytesSent > 0)
- Console.WriteLine(String.Format("Texture upload: {0} / {1}", bytesSent, totalBytesToSend));
- },
-
delegate(bool success, string status, UUID itemID, UUID assetID)
{
Console.WriteLine(String.Format(
diff --git a/Programs/importprimscript/importprimscript.cs b/Programs/importprimscript/importprimscript.cs
index 61d19fa5..94a4847c 100644
--- a/Programs/importprimscript/importprimscript.cs
+++ b/Programs/importprimscript/importprimscript.cs
@@ -216,12 +216,6 @@ namespace importprimscript
AutoResetEvent uploadEvent = new AutoResetEvent(false);
Client.Inventory.RequestCreateItemFromAsset(jp2data, Path.GetFileNameWithoutExtension(filename),
"Uploaded with importprimscript", AssetType.Texture, InventoryType.Texture, UploadFolderID,
-
- delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend)
- {
- // FIXME: Do something with progress?
- },
-
delegate(bool success, string status, UUID itemID, UUID assetID)
{
if (success)