XmlRpcServer.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. namespace Nwc.XmlRpc
  2. {
  3. using System;
  4. using System.Collections;
  5. using System.IO;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Xml;
  11. /// <summary>A restricted HTTP server for use with XML-RPC.</summary>
  12. /// <remarks>It only handles POST requests, and only POSTs representing XML-RPC calls.
  13. /// In addition to dispatching requests it also provides a registry for request handlers.
  14. /// </remarks>
  15. public class XmlRpcServer : IEnumerable
  16. {
  17. #pragma warning disable 0414 // disable "private field assigned but not used"
  18. const int RESPONDER_COUNT = 10;
  19. private TcpListener _myListener;
  20. private int _port;
  21. private IPAddress _address;
  22. private IDictionary _handlers;
  23. private XmlRpcSystemObject _system;
  24. private WaitCallback _wc;
  25. #pragma warning restore 0414
  26. ///<summary>Constructor with port and address.</summary>
  27. ///<remarks>This constructor sets up a TcpListener listening on the
  28. ///given port and address. It also calls a Thread on the method StartListen().</remarks>
  29. ///<param name="address"><c>IPAddress</c> value of the address to listen on.</param>
  30. ///<param name="port"><c>Int</c> value of the port to listen on.</param>
  31. public XmlRpcServer(IPAddress address, int port)
  32. {
  33. _port = port;
  34. _address = address;
  35. _handlers = new Hashtable();
  36. _system = new XmlRpcSystemObject(this);
  37. _wc = new WaitCallback(WaitCallback);
  38. }
  39. ///<summary>Basic constructor.</summary>
  40. ///<remarks>This constructor sets up a TcpListener listening on the
  41. ///given port. It also calls a Thread on the method StartListen(). IPAddress.Any
  42. ///is assumed as the address here.</remarks>
  43. ///<param name="port"><c>Int</c> value of the port to listen on.</param>
  44. public XmlRpcServer(int port) : this(IPAddress.Any, port) { }
  45. /// <summary>Start the server.</summary>
  46. public void Start()
  47. {
  48. try
  49. {
  50. Stop();
  51. //start listing on the given port
  52. // IPAddress addr = IPAddress.Parse("127.0.0.1");
  53. lock (this)
  54. {
  55. _myListener = new TcpListener(IPAddress.Any, _port);
  56. _myListener.Start();
  57. //start the thread which calls the method 'StartListen'
  58. Thread th = new Thread(new ThreadStart(StartListen));
  59. th.Start();
  60. }
  61. }
  62. catch (Exception e)
  63. {
  64. Logger.WriteEntry("An Exception Occurred while Listening :" + e.ToString(), LogLevel.Error);
  65. }
  66. }
  67. /// <summary>Stop the server.</summary>
  68. public void Stop()
  69. {
  70. try
  71. {
  72. if (_myListener != null)
  73. {
  74. lock (this)
  75. {
  76. _myListener.Stop();
  77. _myListener = null;
  78. }
  79. }
  80. }
  81. catch (Exception e)
  82. {
  83. Logger.WriteEntry("An Exception Occurred while stopping :" +
  84. e.ToString(), LogLevel.Error);
  85. }
  86. }
  87. /// <summary>Get an enumeration of my XML-RPC handlers.</summary>
  88. /// <returns><c>IEnumerable</c> the handler enumeration.</returns>
  89. public IEnumerator GetEnumerator()
  90. {
  91. return _handlers.GetEnumerator();
  92. }
  93. /// <summary>Retrieve a handler by name.</summary>
  94. /// <param name="name"><c>String</c> naming a handler</param>
  95. /// <returns><c>Object</c> that is the handler.</returns>
  96. public Object this[String name]
  97. {
  98. get { return _handlers[name]; }
  99. }
  100. ///<summary>
  101. ///This method Accepts new connections and dispatches them when appropriate.
  102. ///</summary>
  103. public void StartListen()
  104. {
  105. while (true && _myListener != null)
  106. {
  107. //Accept a new connection
  108. XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient());
  109. ThreadPool.QueueUserWorkItem(_wc, responder);
  110. }
  111. }
  112. ///<summary>
  113. ///Add an XML-RPC handler object by name.
  114. ///</summary>
  115. ///<param name="name"><c>String</c> XML-RPC dispatch name of this object.</param>
  116. ///<param name="obj"><c>Object</c> The object that is the XML-RPC handler.</param>
  117. public void Add(String name, Object obj)
  118. {
  119. _handlers.Add(name, obj);
  120. }
  121. ///<summary>Return a C# object.method name for and XML-RPC object.method name pair.</summary>
  122. ///<param name="methodName">The XML-RPC object.method.</param>
  123. ///<returns><c>String</c> of form object.method for the underlying C# method.</returns>
  124. public String MethodName(String methodName)
  125. {
  126. int dotAt = methodName.LastIndexOf('.');
  127. if (dotAt == -1)
  128. {
  129. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  130. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName);
  131. }
  132. String objectName = methodName.Substring(0, dotAt);
  133. Object target = _handlers[objectName];
  134. if (target == null)
  135. {
  136. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  137. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
  138. }
  139. return target.GetType().FullName + "." + methodName.Substring(dotAt + 1);
  140. }
  141. ///<summary>Invoke a method described in a request.</summary>
  142. ///<param name="req"><c>XmlRpcRequest</c> containing a method descriptions.</param>
  143. /// <seealso cref="XmlRpcSystemObject.Invoke"/>
  144. /// <seealso cref="XmlRpcServer.Invoke(String,String,IList)"/>
  145. public Object Invoke(XmlRpcRequest req)
  146. {
  147. return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params);
  148. }
  149. ///<summary>Invoke a method on a named handler.</summary>
  150. ///<param name="objectName"><c>String</c> The name of the handler.</param>
  151. ///<param name="methodName"><c>String</c> The name of the method to invoke on the handler.</param>
  152. ///<param name="parameters"><c>IList</c> The parameters to invoke the method with.</param>
  153. /// <seealso cref="XmlRpcSystemObject.Invoke"/>
  154. public Object Invoke(String objectName, String methodName, IList parameters)
  155. {
  156. Object target = _handlers[objectName];
  157. if (target == null)
  158. {
  159. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  160. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
  161. }
  162. return XmlRpcSystemObject.Invoke(target, methodName, parameters);
  163. }
  164. /// <summary>The method the thread pool invokes when a thread is available to handle an HTTP request.</summary>
  165. /// <param name="responder">TcpClient from the socket accept.</param>
  166. public void WaitCallback(object responder)
  167. {
  168. XmlRpcResponder resp = (XmlRpcResponder)responder;
  169. if (resp.HttpReq.HttpMethod == "POST")
  170. {
  171. try
  172. {
  173. resp.Respond();
  174. }
  175. catch (Exception e)
  176. {
  177. Logger.WriteEntry("Failed on post: " + e, LogLevel.Error);
  178. }
  179. }
  180. else
  181. {
  182. Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod +
  183. " ignored", LogLevel.Error);
  184. }
  185. resp.Close();
  186. }
  187. /// <summary>
  188. /// This function send the Header Information to the client (Browser)
  189. /// </summary>
  190. /// <param name="sHttpVersion">HTTP Version</param>
  191. /// <param name="sMIMEHeader">Mime Type</param>
  192. /// <param name="iTotBytes">Total Bytes to be sent in the body</param>
  193. /// <param name="sStatusCode"></param>
  194. /// <param name="output">Socket reference</param>
  195. static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output)
  196. {
  197. String sBuffer = "";
  198. // if Mime type is not provided set default to text/html
  199. if (sMIMEHeader.Length == 0)
  200. {
  201. sMIMEHeader = "text/html"; // Default Mime Type is text/html
  202. }
  203. sBuffer += sHttpVersion + sStatusCode + "\r\n";
  204. sBuffer += "Connection: close\r\n";
  205. if (iTotBytes > 0)
  206. sBuffer += "Content-Length: " + iTotBytes + "\r\n";
  207. sBuffer += "Server: XmlRpcServer \r\n";
  208. sBuffer += "Content-Type: " + sMIMEHeader + "\r\n";
  209. sBuffer += "\r\n";
  210. output.Write(sBuffer);
  211. }
  212. }
  213. }