diff --git a/OpenMetaverse.Http/CapsBase.cs b/OpenMetaverse.Http/CapsBase.cs index 34b3fd30..cdf3f36c 100644 --- a/OpenMetaverse.Http/CapsBase.cs +++ b/OpenMetaverse.Http/CapsBase.cs @@ -134,17 +134,15 @@ namespace OpenMetaverse.Http try { // Get the stream to write our upload to - Stream uploadStream = state.Request.EndGetRequestStream(ar); + using (Stream uploadStream = state.Request.EndGetRequestStream(ar)) + { + // Fire the callback for successfully opening the stream + if (state.OpenWriteCallback != null) + state.OpenWriteCallback(state.Request); - // Fire the callback for successfully opening the stream - if (state.OpenWriteCallback != null) - state.OpenWriteCallback(state.Request); - - // Write our data to the upload stream - uploadStream.Write(state.UploadData, 0, state.UploadData.Length); - // We don't have to call Close with .NET, but Mono 2.4 will hang on - // BeginGetResponse if we don't call it - uploadStream.Close(); + // Write our data to the upload stream + uploadStream.Write(state.UploadData, 0, state.UploadData.Length); + } // Start the request for the remote server response IAsyncResult result = state.Request.BeginGetResponse(GetResponse, state); @@ -154,6 +152,7 @@ namespace OpenMetaverse.Http } catch (Exception ex) { + Logger.Log.Debug("CapsBase.OpenWrite(): " + ex.Message); if (state.CompletedCallback != null) state.CompletedCallback(state.Request, null, null, ex); } @@ -168,49 +167,58 @@ namespace OpenMetaverse.Http 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) + using (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) { - ms.Write(buffer, 0, bytesRead); + responseData = ms.ToArray(); + ms.Close(); } else { - offset += bytesRead; - size -= bytesRead; + responseData = buffer; } - // Fire the download progress callback for each chunk of received data - if (state.DownloadProgressCallback != null) - state.DownloadProgressCallback(state.Request, response, bytesRead, size); + #endregion Read the response + + responseStream.Close(); } - - if (nolength) - responseData = ms.ToArray(); - else - responseData = buffer; - - #endregion Read the response } catch (Exception ex) { + Logger.Log.Debug("CapsBase.GetResponse(): " + ex.Message); error = ex; } @@ -223,6 +231,8 @@ namespace OpenMetaverse.Http if (timedOut) { RequestState requestState = state as RequestState; + Logger.Log.Warn("CapsBase.TimeoutCallback(): Request to " + requestState.Request.RequestUri + + " timed out after " + requestState.MillisecondsTimeout + " milliseconds"); if (requestState != null && requestState.Request != null) requestState.Request.Abort(); } diff --git a/OpenMetaverse.Http/CapsClient.cs b/OpenMetaverse.Http/CapsClient.cs index 3684b4e7..d5ce74b6 100644 --- a/OpenMetaverse.Http/CapsClient.cs +++ b/OpenMetaverse.Http/CapsClient.cs @@ -105,14 +105,14 @@ namespace OpenMetaverse.Http if (postData == null) { // GET - Logger.Log.Debug("[CapsClient] GET " + _Address); + //Logger.Log.Debug("[CapsClient] GET " + _Address); _Request = CapsBase.DownloadStringAsync(_Address, _ClientCert, millisecondsTimeout, DownloadProgressHandler, RequestCompletedHandler); } else { // POST - Logger.Log.Debug("[CapsClient] POST (" + postData.Length + " bytes) " + _Address); + //Logger.Log.Debug("[CapsClient] POST (" + postData.Length + " bytes) " + _Address); _Request = CapsBase.UploadDataAsync(_Address, _ClientCert, contentType, postData, millisecondsTimeout, null, DownloadProgressHandler, RequestCompletedHandler); } diff --git a/OpenMetaverse.Http/CapsServer.cs b/OpenMetaverse.Http/CapsServer.cs index 1df5446d..183d4db2 100644 --- a/OpenMetaverse.Http/CapsServer.cs +++ b/OpenMetaverse.Http/CapsServer.cs @@ -43,8 +43,7 @@ namespace OpenMetaverse.Http /// HTTP request /// HTTP response /// User-defined state object - /// True to send the response and close the connection, false to leave the connection open - public delegate bool CapsRequestCallback(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); + public delegate void CapsRequestCallback(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); public class CapsServer { @@ -53,13 +52,15 @@ namespace OpenMetaverse.Http public CapsRequestCallback LocalCallback; public Uri RemoteHandler; public bool ClientCertRequired; + public bool SendResponseAfterCallback; public object State; - public CapsRedirector(CapsRequestCallback localCallback, Uri remoteHandler, bool clientCertRequired, object state) + public CapsRedirector(CapsRequestCallback localCallback, Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state) { LocalCallback = localCallback; RemoteHandler = remoteHandler; ClientCertRequired = clientCertRequired; + SendResponseAfterCallback = sendResponseAfterCallback; State = state; } } @@ -108,10 +109,21 @@ namespace OpenMetaverse.Http server.RemoveHandler(capsHandler); } + public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state) + { + UUID id = UUID.Random(); + CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, sendResponseAfterCallback, state); + + lock (syncRoot) + fixedCaps.Add(id, redirector); + + return id; + } + public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, object state) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, state); + CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, true, state); lock (syncRoot) fixedCaps.Add(id, redirector); @@ -119,10 +131,10 @@ namespace OpenMetaverse.Http return id; } - public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired) + public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, null); + CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, sendResponseAfterCallback, null); lock (syncRoot) fixedCaps.Add(id, redirector); @@ -130,10 +142,10 @@ namespace OpenMetaverse.Http return id; } - public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, object state, double ttlSeconds) + public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state, double ttlSeconds) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, state); + CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, sendResponseAfterCallback, state); lock (syncRoot) expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds)); @@ -141,10 +153,10 @@ namespace OpenMetaverse.Http return id; } - public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, object state, double ttlSeconds) + public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state, double ttlSeconds) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, state); + CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, sendResponseAfterCallback, state); lock (syncRoot) expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds)); @@ -163,7 +175,7 @@ namespace OpenMetaverse.Http } } - bool CapsCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response) + void CapsCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { UUID capsID; CapsRedirector redirector; @@ -192,14 +204,16 @@ namespace OpenMetaverse.Http if (redirector.RemoteHandler != null) ProxyCapCallback(client, request, response, redirector.RemoteHandler); else - return redirector.LocalCallback(client, request, response, redirector.State); + redirector.LocalCallback(client, request, response, redirector.State); - return true; + if (redirector.SendResponseAfterCallback && !response.Sent) + response.Send(); + return; } } response.Status = HttpStatusCode.NotFound; - return true; + response.Send(); } void ProxyCapCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response, Uri remoteHandler) @@ -223,8 +237,6 @@ namespace OpenMetaverse.Http { while ((numBytes = request.Body.Read(buffer, 0, BUFFER_SIZE)) > 0) writeStream.Write(buffer, 0, numBytes); - - request.Body.Close(); } } @@ -242,16 +254,16 @@ namespace OpenMetaverse.Http { while ((numBytes = readStream.Read(buffer, 0, BUFFER_SIZE)) > 0) response.Body.Write(buffer, 0, numBytes); - - response.Body.Close(); } + + response.Send(); } HttpRequestHandler BuildCapsHandler(string path) { - HttpRequestSignature signature = new HttpRequestSignature(); - signature.Path = path; - return new HttpServer.HttpRequestHandler(signature, CapsCallback); + // All responses have to be send with response.Send() manually to allow proper handling of event queue + // capabilities + return new HttpServer.HttpRequestHandler(new HttpRequestSignature(null, null, path), CapsCallback, false); } } } diff --git a/OpenMetaverse.Http/EventQueueServer.cs b/OpenMetaverse.Http/EventQueueServer.cs index 15f10cbf..4e363914 100644 --- a/OpenMetaverse.Http/EventQueueServer.cs +++ b/OpenMetaverse.Http/EventQueueServer.cs @@ -116,7 +116,7 @@ namespace OpenMetaverse.Http eventQueue.Enqueue(events[i]); } - public bool EventQueueHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response) + public void EventQueueHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response) { // Decode the request OSD osdRequest = null; @@ -161,9 +161,6 @@ namespace OpenMetaverse.Http Thread thread = new Thread(new ThreadStart(EventQueueThread)); thread.IsBackground = true; thread.Start(); - - // Tell HttpServer to leave the connection open - return false; } else { @@ -172,7 +169,7 @@ namespace OpenMetaverse.Http Stop(); response.Connection = request.Connection; - return true; + response.Send(); } } else @@ -182,7 +179,7 @@ namespace OpenMetaverse.Http response.Connection = request.Connection; response.Status = HttpStatusCode.BadRequest; - return true; + response.Send(); } }