2008-12-15 19:13:24 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* Copyright (c) 2007-2008, openmetaverse.org
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
* - Neither the name of the openmetaverse.org nor the names
|
|
|
|
|
|
* 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.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Net;
|
2009-02-09 07:26:50 +00:00
|
|
|
|
using System.Security.Authentication;
|
2008-12-29 20:44:28 +00:00
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
using OpenMetaverse.StructuredData;
|
2008-12-29 20:44:28 +00:00
|
|
|
|
using HttpServer;
|
2009-02-09 07:26:50 +00:00
|
|
|
|
using HttpListener = HttpServer.HttpListener;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
namespace OpenMetaverse.Http
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
2009-02-04 23:00:33 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Delegate for handling incoming HTTP requests through a capability
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="context">Client context</param>
|
|
|
|
|
|
/// <param name="request">HTTP request</param>
|
|
|
|
|
|
/// <param name="response">HTTP response</param>
|
|
|
|
|
|
/// <param name="state">User-defined state object</param>
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public delegate void CapsRequestCallback(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state);
|
2009-02-04 23:00:33 +00:00
|
|
|
|
|
2008-12-15 19:13:24 +00:00
|
|
|
|
public class CapsServer
|
|
|
|
|
|
{
|
2008-12-29 20:44:28 +00:00
|
|
|
|
struct CapsRedirector
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
2009-02-04 23:00:33 +00:00
|
|
|
|
public CapsRequestCallback LocalCallback;
|
2008-12-29 20:44:28 +00:00
|
|
|
|
public Uri RemoteHandler;
|
|
|
|
|
|
public bool ClientCertRequired;
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public bool SendResponseAfterCallback;
|
2009-02-04 23:00:33 +00:00
|
|
|
|
public object State;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public CapsRedirector(CapsRequestCallback localCallback, Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
LocalCallback = localCallback;
|
2008-12-29 20:44:28 +00:00
|
|
|
|
RemoteHandler = remoteHandler;
|
|
|
|
|
|
ClientCertRequired = clientCertRequired;
|
2009-05-21 05:33:40 +00:00
|
|
|
|
SendResponseAfterCallback = sendResponseAfterCallback;
|
2009-02-04 23:00:33 +00:00
|
|
|
|
State = state;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-02-09 07:26:50 +00:00
|
|
|
|
HttpListener server;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
bool serverOwned;
|
2008-12-29 20:44:28 +00:00
|
|
|
|
HttpRequestHandler capsHandler;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
ExpiringCache<UUID, CapsRedirector> expiringCaps = new ExpiringCache<UUID, CapsRedirector>();
|
|
|
|
|
|
Dictionary<UUID, CapsRedirector> fixedCaps = new Dictionary<UUID, CapsRedirector>();
|
|
|
|
|
|
object syncRoot = new object();
|
|
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
public CapsServer(IPAddress address, int port)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
serverOwned = true;
|
2009-02-04 23:00:33 +00:00
|
|
|
|
capsHandler = BuildCapsHandler(@"^/caps/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
|
2009-02-13 22:21:01 +00:00
|
|
|
|
server = HttpListener.Create(log4netLogWriter.Instance, address, port);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
public CapsServer(IPAddress address, int port, X509Certificate sslCertificate, X509Certificate rootCA, bool requireClientCertificate)
|
|
|
|
|
|
{
|
|
|
|
|
|
serverOwned = true;
|
2009-02-04 23:00:33 +00:00
|
|
|
|
capsHandler = BuildCapsHandler(@"^/caps/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
|
2009-02-13 22:21:01 +00:00
|
|
|
|
server = HttpListener.Create(log4netLogWriter.Instance, address, port, sslCertificate, rootCA, SslProtocols.Default, requireClientCertificate);
|
2008-12-29 20:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-02-09 07:26:50 +00:00
|
|
|
|
public CapsServer(HttpListener httpServer, string handlerPath)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
serverOwned = false;
|
|
|
|
|
|
capsHandler = BuildCapsHandler(handlerPath);
|
|
|
|
|
|
server = httpServer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
|
{
|
|
|
|
|
|
server.AddHandler(capsHandler);
|
|
|
|
|
|
|
|
|
|
|
|
if (serverOwned)
|
2009-02-09 07:26:50 +00:00
|
|
|
|
server.Start(10);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (serverOwned)
|
|
|
|
|
|
server.Stop();
|
|
|
|
|
|
|
|
|
|
|
|
server.RemoveHandler(capsHandler);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-02-04 23:00:33 +00:00
|
|
|
|
public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, object state)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
UUID id = UUID.Random();
|
2009-05-21 05:33:40 +00:00
|
|
|
|
CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, true, state);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
fixedCaps.Add(id, redirector);
|
|
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
UUID id = UUID.Random();
|
2009-05-21 05:33:40 +00:00
|
|
|
|
CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, sendResponseAfterCallback, null);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
fixedCaps.Add(id, redirector);
|
|
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public UUID CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state, double ttlSeconds)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
UUID id = UUID.Random();
|
2009-05-21 05:33:40 +00:00
|
|
|
|
CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired, sendResponseAfterCallback, state);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds));
|
|
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, bool sendResponseAfterCallback, object state, double ttlSeconds)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
UUID id = UUID.Random();
|
2009-05-21 05:33:40 +00:00
|
|
|
|
CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired, sendResponseAfterCallback, state);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds));
|
|
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool RemoveCapability(UUID id)
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (expiringCaps.Remove(id))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
else
|
|
|
|
|
|
return fixedCaps.Remove(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
void CapsCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
UUID capsID;
|
|
|
|
|
|
CapsRedirector redirector;
|
|
|
|
|
|
bool success;
|
|
|
|
|
|
|
2009-02-04 23:00:33 +00:00
|
|
|
|
string path = request.Uri.PathAndQuery.TrimEnd('/');
|
|
|
|
|
|
|
|
|
|
|
|
if (UUID.TryParse(path.Substring(path.Length - 36), out capsID))
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
lock (syncRoot)
|
|
|
|
|
|
success = (expiringCaps.TryGetValue(capsID, out redirector) || fixedCaps.TryGetValue(capsID, out redirector));
|
|
|
|
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
|
{
|
2008-12-29 20:44:28 +00:00
|
|
|
|
if (redirector.ClientCertRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false;
|
|
|
|
|
|
// FIXME: Implement this
|
|
|
|
|
|
/*X509Certificate2 clientCert = request.GetClientCertificate();
|
|
|
|
|
|
if (clientCert != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log.Info(clientCert.ToString());
|
|
|
|
|
|
}*/
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (redirector.RemoteHandler != null)
|
|
|
|
|
|
ProxyCapCallback(client, request, response, redirector.RemoteHandler);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
else
|
2009-05-21 05:33:40 +00:00
|
|
|
|
redirector.LocalCallback(client, request, response, redirector.State);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2009-05-21 05:33:40 +00:00
|
|
|
|
if (redirector.SendResponseAfterCallback && !response.Sent)
|
|
|
|
|
|
response.Send();
|
|
|
|
|
|
return;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
response.Status = HttpStatusCode.NotFound;
|
2009-05-21 05:33:40 +00:00
|
|
|
|
response.Send();
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
void ProxyCapCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response, Uri remoteHandler)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
const int BUFFER_SIZE = 2048;
|
|
|
|
|
|
int numBytes;
|
|
|
|
|
|
byte[] buffer = new byte[BUFFER_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
// Proxy the request
|
2008-12-29 20:44:28 +00:00
|
|
|
|
HttpWebRequest remoteRequest = (HttpWebRequest)HttpWebRequest.Create(remoteHandler);
|
|
|
|
|
|
|
|
|
|
|
|
remoteRequest.Method = request.Method;
|
|
|
|
|
|
remoteRequest.Headers.Add(request.Headers);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
// TODO: Support for using our own client certificate during the proxy
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
if (request.Body.Length > 0)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Copy the request stream
|
2008-12-29 20:44:28 +00:00
|
|
|
|
using (Stream writeStream = remoteRequest.GetRequestStream())
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
2008-12-29 20:44:28 +00:00
|
|
|
|
while ((numBytes = request.Body.Read(buffer, 0, BUFFER_SIZE)) > 0)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
writeStream.Write(buffer, 0, numBytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Proxy the response
|
2008-12-29 20:44:28 +00:00
|
|
|
|
HttpWebResponse remoteResponse = (HttpWebResponse)remoteRequest.GetResponse();
|
|
|
|
|
|
|
|
|
|
|
|
response.Status = remoteResponse.StatusCode;
|
|
|
|
|
|
response.Reason = remoteResponse.StatusDescription;
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
2008-12-29 20:44:28 +00:00
|
|
|
|
for (int i = 0; i < remoteResponse.Headers.Count; i++)
|
|
|
|
|
|
response.AddHeader(remoteResponse.Headers.GetKey(i), remoteResponse.Headers[i]);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
// Copy the response stream
|
2008-12-29 20:44:28 +00:00
|
|
|
|
using (Stream readStream = remoteResponse.GetResponseStream())
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
|
while ((numBytes = readStream.Read(buffer, 0, BUFFER_SIZE)) > 0)
|
2008-12-29 20:44:28 +00:00
|
|
|
|
response.Body.Write(buffer, 0, numBytes);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
2009-05-21 05:33:40 +00:00
|
|
|
|
|
|
|
|
|
|
response.Send();
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-02-04 23:00:33 +00:00
|
|
|
|
HttpRequestHandler BuildCapsHandler(string path)
|
2008-12-15 19:13:24 +00:00
|
|
|
|
{
|
2009-05-21 05:33:40 +00:00
|
|
|
|
// 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);
|
2008-12-15 19:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|