namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Xml;
/// A restricted HTTP server for use with XML-RPC.
/// It only handles POST requests, and only POSTs representing XML-RPC calls.
/// In addition to dispatching requests it also provides a registry for request handlers.
///
public class XmlRpcServer : IEnumerable
{
#pragma warning disable 0414 // disable "private field assigned but not used"
const int RESPONDER_COUNT = 10;
private TcpListener _myListener;
private int _port;
private IPAddress _address;
private IDictionary _handlers;
private XmlRpcSystemObject _system;
private WaitCallback _wc;
#pragma warning restore 0414
///Constructor with port and address.
///This constructor sets up a TcpListener listening on the
///given port and address. It also calls a Thread on the method StartListen().
///IPAddress value of the address to listen on.
///Int value of the port to listen on.
public XmlRpcServer(IPAddress address, int port)
{
_port = port;
_address = address;
_handlers = new Hashtable();
_system = new XmlRpcSystemObject(this);
_wc = new WaitCallback(WaitCallback);
}
///Basic constructor.
///This constructor sets up a TcpListener listening on the
///given port. It also calls a Thread on the method StartListen(). IPAddress.Any
///is assumed as the address here.
///Int value of the port to listen on.
public XmlRpcServer(int port) : this(IPAddress.Any, port) { }
/// Start the server.
public void Start()
{
try
{
Stop();
//start listing on the given port
// IPAddress addr = IPAddress.Parse("127.0.0.1");
lock (this)
{
_myListener = new TcpListener(IPAddress.Any, _port);
_myListener.Start();
//start the thread which calls the method 'StartListen'
Thread th = new Thread(new ThreadStart(StartListen));
th.Start();
}
}
catch (Exception e)
{
Logger.WriteEntry("An Exception Occurred while Listening :" + e.ToString(), LogLevel.Error);
}
}
/// Stop the server.
public void Stop()
{
try
{
if (_myListener != null)
{
lock (this)
{
_myListener.Stop();
_myListener = null;
}
}
}
catch (Exception e)
{
Logger.WriteEntry("An Exception Occurred while stopping :" +
e.ToString(), LogLevel.Error);
}
}
/// Get an enumeration of my XML-RPC handlers.
/// IEnumerable the handler enumeration.
public IEnumerator GetEnumerator()
{
return _handlers.GetEnumerator();
}
/// Retrieve a handler by name.
/// String naming a handler
/// Object that is the handler.
public Object this[String name]
{
get { return _handlers[name]; }
}
///
///This method Accepts new connections and dispatches them when appropriate.
///
public void StartListen()
{
while (true && _myListener != null)
{
//Accept a new connection
XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient());
ThreadPool.QueueUserWorkItem(_wc, responder);
}
}
///
///Add an XML-RPC handler object by name.
///
///String XML-RPC dispatch name of this object.
///Object The object that is the XML-RPC handler.
public void Add(String name, Object obj)
{
_handlers.Add(name, obj);
}
///Return a C# object.method name for and XML-RPC object.method name pair.
///The XML-RPC object.method.
///String of form object.method for the underlying C# method.
public String MethodName(String methodName)
{
int dotAt = methodName.LastIndexOf('.');
if (dotAt == -1)
{
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName);
}
String objectName = methodName.Substring(0, dotAt);
Object target = _handlers[objectName];
if (target == null)
{
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
}
return target.GetType().FullName + "." + methodName.Substring(dotAt + 1);
}
///Invoke a method described in a request.
///XmlRpcRequest containing a method descriptions.
///
///
public Object Invoke(XmlRpcRequest req)
{
return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params);
}
///Invoke a method on a named handler.
///String The name of the handler.
///String The name of the method to invoke on the handler.
///IList The parameters to invoke the method with.
///
public Object Invoke(String objectName, String methodName, IList parameters)
{
Object target = _handlers[objectName];
if (target == null)
{
throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
}
return XmlRpcSystemObject.Invoke(target, methodName, parameters);
}
/// The method the thread pool invokes when a thread is available to handle an HTTP request.
/// TcpClient from the socket accept.
public void WaitCallback(object responder)
{
XmlRpcResponder resp = (XmlRpcResponder)responder;
if (resp.HttpReq.HttpMethod == "POST")
{
try
{
resp.Respond();
}
catch (Exception e)
{
Logger.WriteEntry("Failed on post: " + e, LogLevel.Error);
}
}
else
{
Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod +
" ignored", LogLevel.Error);
}
resp.Close();
}
///
/// This function send the Header Information to the client (Browser)
///
/// HTTP Version
/// Mime Type
/// Total Bytes to be sent in the body
///
/// Socket reference
static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output)
{
String sBuffer = "";
// if Mime type is not provided set default to text/html
if (sMIMEHeader.Length == 0)
{
sMIMEHeader = "text/html"; // Default Mime Type is text/html
}
sBuffer += sHttpVersion + sStatusCode + "\r\n";
sBuffer += "Connection: close\r\n";
if (iTotBytes > 0)
sBuffer += "Content-Length: " + iTotBytes + "\r\n";
sBuffer += "Server: XmlRpcServer \r\n";
sBuffer += "Content-Type: " + sMIMEHeader + "\r\n";
sBuffer += "\r\n";
output.Write(sBuffer);
}
}
}