XmlRpcServer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright (c) Contributors, http://www.openmetaverse.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. *
  27. */
  28. namespace Nwc.XmlRpc
  29. {
  30. using System;
  31. using System.Collections;
  32. using System.IO;
  33. using System.Net;
  34. using System.Net.Sockets;
  35. using System.Text;
  36. using System.Threading;
  37. using System.Xml;
  38. /// <summary>A restricted HTTP server for use with XML-RPC.</summary>
  39. /// <remarks>It only handles POST requests, and only POSTs representing XML-RPC calls.
  40. /// In addition to dispatching requests it also provides a registry for request handlers.
  41. /// </remarks>
  42. public class XmlRpcServer : IEnumerable
  43. {
  44. #pragma warning disable 0414 // disable "private field assigned but not used"
  45. const int RESPONDER_COUNT = 10;
  46. private TcpListener _myListener;
  47. private int _port;
  48. private IPAddress _address;
  49. private IDictionary _handlers;
  50. private XmlRpcSystemObject _system;
  51. private WaitCallback _wc;
  52. #pragma warning restore 0414
  53. ///<summary>Constructor with port and address.</summary>
  54. ///<remarks>This constructor sets up a TcpListener listening on the
  55. ///given port and address. It also calls a Thread on the method StartListen().</remarks>
  56. ///<param name="address"><c>IPAddress</c> value of the address to listen on.</param>
  57. ///<param name="port"><c>Int</c> value of the port to listen on.</param>
  58. public XmlRpcServer(IPAddress address, int port)
  59. {
  60. _port = port;
  61. _address = address;
  62. _handlers = new Hashtable();
  63. _system = new XmlRpcSystemObject(this);
  64. _wc = new WaitCallback(WaitCallback);
  65. }
  66. ///<summary>Basic constructor.</summary>
  67. ///<remarks>This constructor sets up a TcpListener listening on the
  68. ///given port. It also calls a Thread on the method StartListen(). IPAddress.Any
  69. ///is assumed as the address here.</remarks>
  70. ///<param name="port"><c>Int</c> value of the port to listen on.</param>
  71. public XmlRpcServer(int port) : this(IPAddress.Any, port) { }
  72. /// <summary>Start the server.</summary>
  73. public void Start()
  74. {
  75. try
  76. {
  77. Stop();
  78. //start listing on the given port
  79. // IPAddress addr = IPAddress.Parse("127.0.0.1");
  80. lock (this)
  81. {
  82. _myListener = new TcpListener(IPAddress.Any, _port);
  83. _myListener.Start();
  84. //start the thread which calls the method 'StartListen'
  85. Thread th = new Thread(new ThreadStart(StartListen));
  86. th.Start();
  87. }
  88. }
  89. catch (Exception e)
  90. {
  91. Logger.WriteEntry("An Exception Occurred while Listening :" + e.ToString(), LogLevel.Error);
  92. }
  93. }
  94. /// <summary>Stop the server.</summary>
  95. public void Stop()
  96. {
  97. try
  98. {
  99. if (_myListener != null)
  100. {
  101. lock (this)
  102. {
  103. _myListener.Stop();
  104. _myListener = null;
  105. }
  106. }
  107. }
  108. catch (Exception e)
  109. {
  110. Logger.WriteEntry("An Exception Occurred while stopping :" +
  111. e.ToString(), LogLevel.Error);
  112. }
  113. }
  114. /// <summary>Get an enumeration of my XML-RPC handlers.</summary>
  115. /// <returns><c>IEnumerable</c> the handler enumeration.</returns>
  116. public IEnumerator GetEnumerator()
  117. {
  118. return _handlers.GetEnumerator();
  119. }
  120. /// <summary>Retrieve a handler by name.</summary>
  121. /// <param name="name"><c>String</c> naming a handler</param>
  122. /// <returns><c>Object</c> that is the handler.</returns>
  123. public Object this[String name]
  124. {
  125. get { return _handlers[name]; }
  126. }
  127. ///<summary>
  128. ///This method Accepts new connections and dispatches them when appropriate.
  129. ///</summary>
  130. public void StartListen()
  131. {
  132. while (true && _myListener != null)
  133. {
  134. //Accept a new connection
  135. XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient());
  136. ThreadPool.QueueUserWorkItem(_wc, responder);
  137. }
  138. }
  139. ///<summary>
  140. ///Add an XML-RPC handler object by name.
  141. ///</summary>
  142. ///<param name="name"><c>String</c> XML-RPC dispatch name of this object.</param>
  143. ///<param name="obj"><c>Object</c> The object that is the XML-RPC handler.</param>
  144. public void Add(String name, Object obj)
  145. {
  146. _handlers.Add(name, obj);
  147. }
  148. ///<summary>Return a C# object.method name for and XML-RPC object.method name pair.</summary>
  149. ///<param name="methodName">The XML-RPC object.method.</param>
  150. ///<returns><c>String</c> of form object.method for the underlying C# method.</returns>
  151. public String MethodName(String methodName)
  152. {
  153. int dotAt = methodName.LastIndexOf('.');
  154. if (dotAt == -1)
  155. {
  156. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  157. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName);
  158. }
  159. String objectName = methodName.Substring(0, dotAt);
  160. Object target = _handlers[objectName];
  161. if (target == null)
  162. {
  163. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  164. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
  165. }
  166. return target.GetType().FullName + "." + methodName.Substring(dotAt + 1);
  167. }
  168. ///<summary>Invoke a method described in a request.</summary>
  169. ///<param name="req"><c>XmlRpcRequest</c> containing a method descriptions.</param>
  170. /// <seealso cref="XmlRpcSystemObject.Invoke"/>
  171. /// <seealso cref="XmlRpcServer.Invoke(String,String,IList)"/>
  172. public Object Invoke(XmlRpcRequest req)
  173. {
  174. return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params);
  175. }
  176. ///<summary>Invoke a method on a named handler.</summary>
  177. ///<param name="objectName"><c>String</c> The name of the handler.</param>
  178. ///<param name="methodName"><c>String</c> The name of the method to invoke on the handler.</param>
  179. ///<param name="parameters"><c>IList</c> The parameters to invoke the method with.</param>
  180. /// <seealso cref="XmlRpcSystemObject.Invoke"/>
  181. public Object Invoke(String objectName, String methodName, IList parameters)
  182. {
  183. Object target = _handlers[objectName];
  184. if (target == null)
  185. {
  186. throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  187. XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found");
  188. }
  189. return XmlRpcSystemObject.Invoke(target, methodName, parameters);
  190. }
  191. /// <summary>The method the thread pool invokes when a thread is available to handle an HTTP request.</summary>
  192. /// <param name="responder">TcpClient from the socket accept.</param>
  193. public void WaitCallback(object responder)
  194. {
  195. XmlRpcResponder resp = (XmlRpcResponder)responder;
  196. if (resp.HttpReq.HttpMethod == "POST")
  197. {
  198. try
  199. {
  200. resp.Respond();
  201. }
  202. catch (Exception e)
  203. {
  204. Logger.WriteEntry("Failed on post: " + e, LogLevel.Error);
  205. }
  206. }
  207. else
  208. {
  209. Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod +
  210. " ignored", LogLevel.Error);
  211. }
  212. resp.Close();
  213. }
  214. /// <summary>
  215. /// This function send the Header Information to the client (Browser)
  216. /// </summary>
  217. /// <param name="sHttpVersion">HTTP Version</param>
  218. /// <param name="sMIMEHeader">Mime Type</param>
  219. /// <param name="iTotBytes">Total Bytes to be sent in the body</param>
  220. /// <param name="sStatusCode"></param>
  221. /// <param name="output">Socket reference</param>
  222. static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output)
  223. {
  224. String sBuffer = "";
  225. // if Mime type is not provided set default to text/html
  226. if (sMIMEHeader.Length == 0)
  227. {
  228. sMIMEHeader = "text/html"; // Default Mime Type is text/html
  229. }
  230. sBuffer += sHttpVersion + sStatusCode + "\r\n";
  231. sBuffer += "Connection: close\r\n";
  232. if (iTotBytes > 0)
  233. sBuffer += "Content-Length: " + iTotBytes + "\r\n";
  234. sBuffer += "Server: XmlRpcServer \r\n";
  235. sBuffer += "Content-Type: " + sMIMEHeader + "\r\n";
  236. sBuffer += "\r\n";
  237. output.Write(sBuffer);
  238. }
  239. }
  240. }