BaseHttpServer.cs 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.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 OpenSimulator 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. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Collections.Specialized;
  31. using System.IO;
  32. using System.Net;
  33. using System.Net.Sockets;
  34. using System.Reflection;
  35. using System.Globalization;
  36. using System.Text;
  37. using System.Threading;
  38. using System.Xml;
  39. using HttpServer;
  40. using log4net;
  41. using Nwc.XmlRpc;
  42. using OpenMetaverse.StructuredData;
  43. using CoolHTTPListener = HttpServer.HttpListener;
  44. using HttpListener=System.Net.HttpListener;
  45. using LogPrio=HttpServer.LogPrio;
  46. namespace OpenSim.Framework.Servers.HttpServer
  47. {
  48. public class BaseHttpServer : IHttpServer
  49. {
  50. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  51. private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
  52. private volatile int NotSocketErrors = 0;
  53. public volatile bool HTTPDRunning = false;
  54. protected Thread m_workerThread;
  55. // protected HttpListener m_httpListener;
  56. protected CoolHTTPListener m_httpListener2;
  57. protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
  58. protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
  59. protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
  60. protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
  61. protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
  62. protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
  63. protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
  64. protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
  65. new Dictionary<string, PollServiceEventArgs>();
  66. protected uint m_port;
  67. protected uint m_sslport;
  68. protected bool m_ssl;
  69. protected bool m_firstcaps = true;
  70. protected string m_SSLCommonName = "";
  71. protected IPAddress m_listenIPAddress = IPAddress.Any;
  72. private PollServiceRequestManager m_PollServiceManager;
  73. public uint SSLPort
  74. {
  75. get { return m_sslport; }
  76. }
  77. public string SSLCommonName
  78. {
  79. get { return m_SSLCommonName; }
  80. }
  81. public uint Port
  82. {
  83. get { return m_port; }
  84. }
  85. public bool UseSSL
  86. {
  87. get { return m_ssl; }
  88. }
  89. public IPAddress ListenIPAddress
  90. {
  91. get { return m_listenIPAddress; }
  92. set { m_listenIPAddress = value; }
  93. }
  94. public BaseHttpServer(uint port)
  95. {
  96. m_port = port;
  97. }
  98. public BaseHttpServer(uint port, bool ssl) : this (port)
  99. {
  100. m_ssl = ssl;
  101. }
  102. public BaseHttpServer(uint port, bool ssl, uint sslport, string CN) : this (port, ssl)
  103. {
  104. if (m_ssl)
  105. {
  106. m_sslport = sslport;
  107. }
  108. }
  109. /// <summary>
  110. /// Add a stream handler to the http server. If the handler already exists, then nothing happens.
  111. /// </summary>
  112. /// <param name="handler"></param>
  113. public void AddStreamHandler(IRequestHandler handler)
  114. {
  115. string httpMethod = handler.HttpMethod;
  116. string path = handler.Path;
  117. string handlerKey = GetHandlerKey(httpMethod, path);
  118. lock (m_streamHandlers)
  119. {
  120. if (!m_streamHandlers.ContainsKey(handlerKey))
  121. {
  122. // m_log.DebugFormat("[BASE HTTP SERVER]: Adding handler key {0}", handlerKey);
  123. m_streamHandlers.Add(handlerKey, handler);
  124. }
  125. }
  126. }
  127. public List<string> GetStreamHandlerKeys()
  128. {
  129. return new List<string>(m_streamHandlers.Keys);
  130. }
  131. private static string GetHandlerKey(string httpMethod, string path)
  132. {
  133. return httpMethod + ":" + path;
  134. }
  135. public bool AddXmlRPCHandler(string method, XmlRpcMethod handler)
  136. {
  137. return AddXmlRPCHandler(method, handler, true);
  138. }
  139. public bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive)
  140. {
  141. lock (m_rpcHandlers)
  142. {
  143. m_rpcHandlers[method] = handler;
  144. m_rpcHandlersKeepAlive[method] = keepAlive; // default
  145. }
  146. return true;
  147. }
  148. public XmlRpcMethod GetXmlRPCHandler(string method)
  149. {
  150. lock (m_rpcHandlers)
  151. {
  152. if (m_rpcHandlers.ContainsKey(method))
  153. {
  154. return m_rpcHandlers[method];
  155. }
  156. else
  157. {
  158. return null;
  159. }
  160. }
  161. }
  162. public List<string> GetXmlRpcHandlerKeys()
  163. {
  164. return new List<string>(m_rpcHandlers.Keys);
  165. }
  166. public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
  167. {
  168. //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
  169. lock (m_HTTPHandlers)
  170. {
  171. if (!m_HTTPHandlers.ContainsKey(methodName))
  172. {
  173. m_HTTPHandlers.Add(methodName, handler);
  174. return true;
  175. }
  176. }
  177. //must already have a handler for that path so return false
  178. return false;
  179. }
  180. public List<string> GetHTTPHandlerKeys()
  181. {
  182. return new List<string>(m_HTTPHandlers.Keys);
  183. }
  184. public bool AddPollServiceHTTPHandler(string methodName, GenericHTTPMethod handler, PollServiceEventArgs args)
  185. {
  186. bool pollHandlerResult = false;
  187. lock (m_pollHandlers)
  188. {
  189. if (!m_pollHandlers.ContainsKey(methodName))
  190. {
  191. m_pollHandlers.Add(methodName,args);
  192. pollHandlerResult = true;
  193. }
  194. }
  195. if (pollHandlerResult)
  196. return AddHTTPHandler(methodName, handler);
  197. return false;
  198. }
  199. public List<string> GetPollServiceHandlerKeys()
  200. {
  201. return new List<string>(m_pollHandlers.Keys);
  202. }
  203. // Note that the agent string is provided simply to differentiate
  204. // the handlers - it is NOT required to be an actual agent header
  205. // value.
  206. public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
  207. {
  208. lock (m_agentHandlers)
  209. {
  210. if (!m_agentHandlers.ContainsKey(agent))
  211. {
  212. m_agentHandlers.Add(agent, handler);
  213. return true;
  214. }
  215. }
  216. //must already have a handler for that path so return false
  217. return false;
  218. }
  219. public List<string> GetAgentHandlerKeys()
  220. {
  221. return new List<string>(m_agentHandlers.Keys);
  222. }
  223. public bool AddLLSDHandler(string path, LLSDMethod handler)
  224. {
  225. lock (m_llsdHandlers)
  226. {
  227. if (!m_llsdHandlers.ContainsKey(path))
  228. {
  229. m_llsdHandlers.Add(path, handler);
  230. return true;
  231. }
  232. }
  233. return false;
  234. }
  235. public List<string> GetLLSDHandlerKeys()
  236. {
  237. return new List<string>(m_llsdHandlers.Keys);
  238. }
  239. public bool SetDefaultLLSDHandler(DefaultLLSDMethod handler)
  240. {
  241. m_defaultLlsdHandler = handler;
  242. return true;
  243. }
  244. private void OnRequest(object source, RequestEventArgs args)
  245. {
  246. try
  247. {
  248. IHttpClientContext context = (IHttpClientContext)source;
  249. IHttpRequest request = args.Request;
  250. PollServiceEventArgs psEvArgs;
  251. if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs))
  252. {
  253. PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request);
  254. if (psEvArgs.Request != null)
  255. {
  256. OSHttpRequest req = new OSHttpRequest(context, request);
  257. Stream requestStream = req.InputStream;
  258. Encoding encoding = Encoding.UTF8;
  259. StreamReader reader = new StreamReader(requestStream, encoding);
  260. string requestBody = reader.ReadToEnd();
  261. Hashtable keysvals = new Hashtable();
  262. Hashtable headervals = new Hashtable();
  263. string[] querystringkeys = req.QueryString.AllKeys;
  264. string[] rHeaders = req.Headers.AllKeys;
  265. keysvals.Add("body", requestBody);
  266. keysvals.Add("uri", req.RawUrl);
  267. keysvals.Add("content-type", req.ContentType);
  268. keysvals.Add("http-method", req.HttpMethod);
  269. foreach (string queryname in querystringkeys)
  270. {
  271. keysvals.Add(queryname, req.QueryString[queryname]);
  272. }
  273. foreach (string headername in rHeaders)
  274. {
  275. headervals[headername] = req.Headers[headername];
  276. }
  277. keysvals.Add("headers", headervals);
  278. keysvals.Add("querystringkeys", querystringkeys);
  279. psEvArgs.Request(psreq.RequestID, keysvals);
  280. }
  281. m_PollServiceManager.Enqueue(psreq);
  282. }
  283. else
  284. {
  285. OnHandleRequestIOThread(context, request);
  286. }
  287. }
  288. catch (Exception e)
  289. {
  290. m_log.ErrorFormat("[BASE HTTP SERVER]: OnRequest() failed with {0}{1}", e.Message, e.StackTrace);
  291. }
  292. }
  293. public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
  294. {
  295. OSHttpRequest req = new OSHttpRequest(context, request);
  296. OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
  297. HandleRequest(req, resp);
  298. // !!!HACK ALERT!!!
  299. // There seems to be a bug in the underlying http code that makes subsequent requests
  300. // come up with trash in Accept headers. Until that gets fixed, we're cleaning them up here.
  301. if (request.AcceptTypes != null)
  302. for (int i = 0; i < request.AcceptTypes.Length; i++)
  303. request.AcceptTypes[i] = string.Empty;
  304. }
  305. // public void ConvertIHttpClientContextToOSHttp(object stateinfo)
  306. // {
  307. // HttpServerContextObj objstate = (HttpServerContextObj)stateinfo;
  308. // OSHttpRequest request = objstate.oreq;
  309. // OSHttpResponse resp = objstate.oresp;
  310. // HandleRequest(request,resp);
  311. // }
  312. /// <summary>
  313. /// This methods is the start of incoming HTTP request handling.
  314. /// </summary>
  315. /// <param name="request"></param>
  316. /// <param name="response"></param>
  317. public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response)
  318. {
  319. if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread
  320. {
  321. try
  322. {
  323. SendHTML500(response);
  324. }
  325. catch
  326. {
  327. }
  328. return;
  329. }
  330. string requestMethod = request.HttpMethod;
  331. string uriString = request.RawUrl;
  332. string reqnum = "unknown";
  333. int tickstart = Environment.TickCount;
  334. try
  335. {
  336. // OpenSim.Framework.WebUtil.OSHeaderRequestID
  337. if (request.Headers["opensim-request-id"] != null)
  338. reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
  339. // m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
  340. Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
  341. // This is the REST agent interface. We require an agent to properly identify
  342. // itself. If the REST handler recognizes the prefix it will attempt to
  343. // satisfy the request. If it is not recognizable, and no damage has occurred
  344. // the request can be passed through to the other handlers. This is a low
  345. // probability event; if a request is matched it is normally expected to be
  346. // handled
  347. IHttpAgentHandler agentHandler;
  348. if (TryGetAgentHandler(request, response, out agentHandler))
  349. {
  350. if (HandleAgentRequest(agentHandler, request, response))
  351. {
  352. return;
  353. }
  354. }
  355. //response.KeepAlive = true;
  356. response.SendChunked = false;
  357. IRequestHandler requestHandler;
  358. string path = request.RawUrl;
  359. string handlerKey = GetHandlerKey(request.HttpMethod, path);
  360. // m_log.DebugFormat("[BASE HTTP SERVER]: Handling {0} request for {1}", request.HttpMethod, path);
  361. if (TryGetStreamHandler(handlerKey, out requestHandler))
  362. {
  363. //m_log.Debug("[BASE HTTP SERVER]: Found Stream Handler");
  364. // Okay, so this is bad, but should be considered temporary until everything is IStreamHandler.
  365. byte[] buffer = null;
  366. response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
  367. if (requestHandler is IStreamedRequestHandler)
  368. {
  369. IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
  370. buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response);
  371. }
  372. else if (requestHandler is IGenericHTTPHandler)
  373. {
  374. //m_log.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler");
  375. IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler;
  376. Stream requestStream = request.InputStream;
  377. Encoding encoding = Encoding.UTF8;
  378. StreamReader reader = new StreamReader(requestStream, encoding);
  379. string requestBody = reader.ReadToEnd();
  380. reader.Close();
  381. //requestStream.Close();
  382. Hashtable keysvals = new Hashtable();
  383. Hashtable headervals = new Hashtable();
  384. //string host = String.Empty;
  385. string[] querystringkeys = request.QueryString.AllKeys;
  386. string[] rHeaders = request.Headers.AllKeys;
  387. foreach (string queryname in querystringkeys)
  388. {
  389. keysvals.Add(queryname, request.QueryString[queryname]);
  390. }
  391. foreach (string headername in rHeaders)
  392. {
  393. //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]);
  394. headervals[headername] = request.Headers[headername];
  395. }
  396. // if (headervals.Contains("Host"))
  397. // {
  398. // host = (string)headervals["Host"];
  399. // }
  400. keysvals.Add("requestbody", requestBody);
  401. keysvals.Add("headers",headervals);
  402. if (keysvals.Contains("method"))
  403. {
  404. //m_log.Warn("[HTTP]: Contains Method");
  405. //string method = (string)keysvals["method"];
  406. //m_log.Warn("[HTTP]: " + requestBody);
  407. }
  408. DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response);
  409. return;
  410. }
  411. else
  412. {
  413. IStreamHandler streamHandler = (IStreamHandler)requestHandler;
  414. using (MemoryStream memoryStream = new MemoryStream())
  415. {
  416. streamHandler.Handle(path, request.InputStream, memoryStream, request, response);
  417. memoryStream.Flush();
  418. buffer = memoryStream.ToArray();
  419. }
  420. }
  421. request.InputStream.Close();
  422. // HTTP IN support. The script engine takes it from here
  423. // Nothing to worry about for us.
  424. //
  425. if (buffer == null)
  426. return;
  427. if (!response.SendChunked)
  428. response.ContentLength64 = buffer.LongLength;
  429. try
  430. {
  431. response.OutputStream.Write(buffer, 0, buffer.Length);
  432. //response.OutputStream.Close();
  433. }
  434. catch (HttpListenerException)
  435. {
  436. m_log.WarnFormat("[BASE HTTP SERVER]: HTTP request abnormally terminated.");
  437. }
  438. //response.OutputStream.Close();
  439. try
  440. {
  441. response.Send();
  442. //response.FreeContext();
  443. }
  444. catch (SocketException e)
  445. {
  446. // This has to be here to prevent a Linux/Mono crash
  447. m_log.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  448. }
  449. catch (IOException e)
  450. {
  451. m_log.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e.Message);
  452. }
  453. return;
  454. }
  455. if (request.AcceptTypes != null && request.AcceptTypes.Length > 0)
  456. {
  457. foreach (string strAccept in request.AcceptTypes)
  458. {
  459. if (strAccept.Contains("application/llsd+xml") ||
  460. strAccept.Contains("application/llsd+json"))
  461. {
  462. //m_log.Info("[Debug BASE HTTP SERVER]: Found an application/llsd+xml accept header");
  463. HandleLLSDRequests(request, response);
  464. return;
  465. }
  466. }
  467. }
  468. switch (request.ContentType)
  469. {
  470. case null:
  471. case "text/html":
  472. // m_log.DebugFormat(
  473. // "[BASE HTTP SERVER]: Found a text/html content type for request {0}", request.RawUrl);
  474. HandleHTTPRequest(request, response);
  475. return;
  476. case "application/llsd+xml":
  477. case "application/xml+llsd":
  478. case "application/llsd+json":
  479. //m_log.Info("[Debug BASE HTTP SERVER]: found a application/llsd+xml content type");
  480. HandleLLSDRequests(request, response);
  481. return;
  482. case "text/xml":
  483. case "application/xml":
  484. case "application/json":
  485. default:
  486. //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
  487. // Point of note.. the DoWeHaveA methods check for an EXACT path
  488. // if (request.RawUrl.Contains("/CAPS/EQG"))
  489. // {
  490. // int i = 1;
  491. // }
  492. //m_log.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler");
  493. if (DoWeHaveALLSDHandler(request.RawUrl))
  494. {
  495. //m_log.Info("[Debug BASE HTTP SERVER]: Found LLSD Handler");
  496. HandleLLSDRequests(request, response);
  497. return;
  498. }
  499. // m_log.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl);
  500. if (DoWeHaveAHTTPHandler(request.RawUrl))
  501. {
  502. // m_log.DebugFormat("[BASE HTTP SERVER]: Found HTTP Handler for request {0}", request.RawUrl);
  503. HandleHTTPRequest(request, response);
  504. return;
  505. }
  506. //m_log.Info("[Debug BASE HTTP SERVER]: Generic XMLRPC");
  507. // generic login request.
  508. HandleXmlRpcRequests(request, response);
  509. return;
  510. }
  511. }
  512. catch (SocketException e)
  513. {
  514. // At least on linux, it appears that if the client makes a request without requiring the response,
  515. // an unconnected socket exception is thrown when we close the response output stream. There's no
  516. // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore
  517. // the exception instead.
  518. //
  519. // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go
  520. // with the minimum first
  521. m_log.WarnFormat("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux", e);
  522. }
  523. catch (IOException e)
  524. {
  525. m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw ", e);
  526. }
  527. catch (Exception e)
  528. {
  529. m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw " + e.ToString());
  530. SendHTML500(response);
  531. }
  532. finally
  533. {
  534. // Every month or so this will wrap and give bad numbers, not really a problem
  535. // since its just for reporting, tickdiff limit can be adjusted
  536. int tickdiff = Environment.TickCount - tickstart;
  537. if (tickdiff > 3000)
  538. m_log.InfoFormat(
  539. "[BASE HTTP SERVER]: slow {0} request for {1} from {2} took {3} ms", requestMethod, uriString, request.RemoteIPEndPoint.ToString(), tickdiff);
  540. }
  541. }
  542. private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler)
  543. {
  544. string bestMatch = null;
  545. lock (m_streamHandlers)
  546. {
  547. foreach (string pattern in m_streamHandlers.Keys)
  548. {
  549. if (handlerKey.StartsWith(pattern))
  550. {
  551. if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
  552. {
  553. bestMatch = pattern;
  554. }
  555. }
  556. }
  557. if (String.IsNullOrEmpty(bestMatch))
  558. {
  559. streamHandler = null;
  560. return false;
  561. }
  562. else
  563. {
  564. streamHandler = m_streamHandlers[bestMatch];
  565. return true;
  566. }
  567. }
  568. }
  569. private bool TryGetPollServiceHTTPHandler(string handlerKey, out PollServiceEventArgs oServiceEventArgs)
  570. {
  571. string bestMatch = null;
  572. lock (m_pollHandlers)
  573. {
  574. foreach (string pattern in m_pollHandlers.Keys)
  575. {
  576. if (handlerKey.StartsWith(pattern))
  577. {
  578. if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
  579. {
  580. bestMatch = pattern;
  581. }
  582. }
  583. }
  584. if (String.IsNullOrEmpty(bestMatch))
  585. {
  586. oServiceEventArgs = null;
  587. return false;
  588. }
  589. else
  590. {
  591. oServiceEventArgs = m_pollHandlers[bestMatch];
  592. return true;
  593. }
  594. }
  595. }
  596. private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler)
  597. {
  598. // m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey);
  599. string bestMatch = null;
  600. lock (m_HTTPHandlers)
  601. {
  602. foreach (string pattern in m_HTTPHandlers.Keys)
  603. {
  604. if (handlerKey.StartsWith(pattern))
  605. {
  606. if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
  607. {
  608. bestMatch = pattern;
  609. }
  610. }
  611. }
  612. if (String.IsNullOrEmpty(bestMatch))
  613. {
  614. HTTPHandler = null;
  615. return false;
  616. }
  617. else
  618. {
  619. HTTPHandler = m_HTTPHandlers[bestMatch];
  620. return true;
  621. }
  622. }
  623. }
  624. private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
  625. {
  626. agentHandler = null;
  627. try
  628. {
  629. foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
  630. {
  631. if (handler.Match(request, response))
  632. {
  633. agentHandler = handler;
  634. return true;
  635. }
  636. }
  637. }
  638. catch(KeyNotFoundException)
  639. {
  640. }
  641. return false;
  642. }
  643. /// <summary>
  644. /// Try all the registered xmlrpc handlers when an xmlrpc request is received.
  645. /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method.
  646. /// </summary>
  647. /// <param name="request"></param>
  648. /// <param name="response"></param>
  649. private void HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response)
  650. {
  651. Stream requestStream = request.InputStream;
  652. Encoding encoding = Encoding.UTF8;
  653. StreamReader reader = new StreamReader(requestStream, encoding);
  654. string requestBody = reader.ReadToEnd();
  655. reader.Close();
  656. requestStream.Close();
  657. //m_log.Debug(requestBody);
  658. requestBody = requestBody.Replace("<base64></base64>", "");
  659. string responseString = String.Empty;
  660. XmlRpcRequest xmlRprcRequest = null;
  661. try
  662. {
  663. xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody);
  664. }
  665. catch (XmlException)
  666. {
  667. }
  668. if (xmlRprcRequest != null)
  669. {
  670. string methodName = xmlRprcRequest.MethodName;
  671. if (methodName != null)
  672. {
  673. xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1]
  674. XmlRpcResponse xmlRpcResponse;
  675. XmlRpcMethod method;
  676. bool methodWasFound;
  677. lock (m_rpcHandlers)
  678. {
  679. methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method);
  680. }
  681. if (methodWasFound)
  682. {
  683. xmlRprcRequest.Params.Add(request.Url); // Param[2]
  684. string xff = "X-Forwarded-For";
  685. string xfflower = xff.ToLower();
  686. foreach (string s in request.Headers.AllKeys)
  687. {
  688. if (s != null && s.Equals(xfflower))
  689. {
  690. xff = xfflower;
  691. break;
  692. }
  693. }
  694. xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3]
  695. try
  696. {
  697. xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint);
  698. }
  699. catch(Exception e)
  700. {
  701. string errorMessage
  702. = String.Format(
  703. "Requested method [{0}] from {1} threw exception: {2} {3}",
  704. methodName, request.RemoteIPEndPoint.Address, e.Message, e.StackTrace);
  705. m_log.ErrorFormat("[BASE HTTP SERVER]: {0}", errorMessage);
  706. // if the registered XmlRpc method threw an exception, we pass a fault-code along
  707. xmlRpcResponse = new XmlRpcResponse();
  708. // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
  709. xmlRpcResponse.SetFault(-32603, errorMessage);
  710. }
  711. // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here
  712. response.KeepAlive = m_rpcHandlersKeepAlive[methodName];
  713. }
  714. else
  715. {
  716. xmlRpcResponse = new XmlRpcResponse();
  717. // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
  718. xmlRpcResponse.SetFault(
  719. XmlRpcErrorCodes.SERVER_ERROR_METHOD,
  720. String.Format("Requested method [{0}] not found", methodName));
  721. }
  722. responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse);
  723. }
  724. else
  725. {
  726. //HandleLLSDRequests(request, response);
  727. response.ContentType = "text/plain";
  728. response.StatusCode = 404;
  729. response.StatusDescription = "Not Found";
  730. response.ProtocolVersion = "HTTP/1.0";
  731. byte[] buf = Encoding.UTF8.GetBytes("Not found");
  732. response.KeepAlive = false;
  733. m_log.ErrorFormat("[BASE HTTP SERVER]: Handler not found for http request {0}", request.RawUrl);
  734. response.SendChunked = false;
  735. response.ContentLength64 = buf.Length;
  736. response.ContentEncoding = Encoding.UTF8;
  737. try
  738. {
  739. response.OutputStream.Write(buf, 0, buf.Length);
  740. }
  741. catch (Exception ex)
  742. {
  743. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  744. }
  745. finally
  746. {
  747. try
  748. {
  749. response.Send();
  750. //response.FreeContext();
  751. }
  752. catch (SocketException e)
  753. {
  754. // This has to be here to prevent a Linux/Mono crash
  755. m_log.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  756. }
  757. catch (IOException e)
  758. {
  759. m_log.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e.Message);
  760. }
  761. }
  762. return;
  763. //responseString = "Error";
  764. }
  765. }
  766. response.ContentType = "text/xml";
  767. byte[] buffer = Encoding.UTF8.GetBytes(responseString);
  768. response.SendChunked = false;
  769. response.ContentLength64 = buffer.Length;
  770. response.ContentEncoding = Encoding.UTF8;
  771. try
  772. {
  773. response.OutputStream.Write(buffer, 0, buffer.Length);
  774. }
  775. catch (Exception ex)
  776. {
  777. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  778. }
  779. finally
  780. {
  781. try
  782. {
  783. response.Send();
  784. //response.FreeContext();
  785. }
  786. catch (SocketException e)
  787. {
  788. // This has to be here to prevent a Linux/Mono crash
  789. m_log.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  790. }
  791. catch (IOException e)
  792. {
  793. m_log.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e.Message);
  794. }
  795. }
  796. }
  797. private void HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
  798. {
  799. //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
  800. Stream requestStream = request.InputStream;
  801. Encoding encoding = Encoding.UTF8;
  802. StreamReader reader = new StreamReader(requestStream, encoding);
  803. string requestBody = reader.ReadToEnd();
  804. reader.Close();
  805. requestStream.Close();
  806. //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody);
  807. response.KeepAlive = true;
  808. OSD llsdRequest = null;
  809. OSD llsdResponse = null;
  810. bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest"));
  811. if (requestBody.Length == 0)
  812. // Get Request
  813. {
  814. requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>";
  815. }
  816. try
  817. {
  818. llsdRequest = OSDParser.Deserialize(requestBody);
  819. }
  820. catch (Exception ex)
  821. {
  822. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  823. }
  824. if (llsdRequest != null)// && m_defaultLlsdHandler != null)
  825. {
  826. LLSDMethod llsdhandler = null;
  827. if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV)
  828. {
  829. // we found a registered llsd handler to service this request
  830. llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString());
  831. }
  832. else
  833. {
  834. // we didn't find a registered llsd handler to service this request
  835. // check if we have a default llsd handler
  836. if (m_defaultLlsdHandler != null)
  837. {
  838. // LibOMV path
  839. llsdResponse = m_defaultLlsdHandler(llsdRequest, request.RemoteIPEndPoint);
  840. }
  841. else
  842. {
  843. // Oops, no handler for this.. give em the failed message
  844. llsdResponse = GenerateNoLLSDHandlerResponse();
  845. }
  846. }
  847. }
  848. else
  849. {
  850. llsdResponse = GenerateNoLLSDHandlerResponse();
  851. }
  852. byte[] buffer = new byte[0];
  853. if (llsdResponse.ToString() == "shutdown404!")
  854. {
  855. response.ContentType = "text/plain";
  856. response.StatusCode = 404;
  857. response.StatusDescription = "Not Found";
  858. response.ProtocolVersion = "HTTP/1.0";
  859. buffer = Encoding.UTF8.GetBytes("Not found");
  860. }
  861. else
  862. {
  863. // Select an appropriate response format
  864. buffer = BuildLLSDResponse(request, response, llsdResponse);
  865. }
  866. response.SendChunked = false;
  867. response.ContentLength64 = buffer.Length;
  868. response.ContentEncoding = Encoding.UTF8;
  869. response.KeepAlive = true;
  870. try
  871. {
  872. response.OutputStream.Write(buffer, 0, buffer.Length);
  873. }
  874. catch (Exception ex)
  875. {
  876. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  877. }
  878. finally
  879. {
  880. //response.OutputStream.Close();
  881. try
  882. {
  883. response.Send();
  884. response.OutputStream.Flush();
  885. //response.FreeContext();
  886. //response.OutputStream.Close();
  887. }
  888. catch (IOException e)
  889. {
  890. m_log.WarnFormat("[BASE HTTP SERVER]: LLSD IOException {0}.", e);
  891. }
  892. catch (SocketException e)
  893. {
  894. // This has to be here to prevent a Linux/Mono crash
  895. m_log.WarnFormat("[BASE HTTP SERVER]: LLSD issue {0}.\nNOTE: this may be spurious on Linux.", e);
  896. }
  897. }
  898. }
  899. private byte[] BuildLLSDResponse(OSHttpRequest request, OSHttpResponse response, OSD llsdResponse)
  900. {
  901. if (request.AcceptTypes != null && request.AcceptTypes.Length > 0)
  902. {
  903. foreach (string strAccept in request.AcceptTypes)
  904. {
  905. switch (strAccept)
  906. {
  907. case "application/llsd+xml":
  908. case "application/xml":
  909. case "text/xml":
  910. response.ContentType = strAccept;
  911. return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
  912. case "application/llsd+json":
  913. case "application/json":
  914. response.ContentType = strAccept;
  915. return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
  916. }
  917. }
  918. }
  919. if (!String.IsNullOrEmpty(request.ContentType))
  920. {
  921. switch (request.ContentType)
  922. {
  923. case "application/llsd+xml":
  924. case "application/xml":
  925. case "text/xml":
  926. response.ContentType = request.ContentType;
  927. return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
  928. case "application/llsd+json":
  929. case "application/json":
  930. response.ContentType = request.ContentType;
  931. return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
  932. }
  933. }
  934. // response.ContentType = "application/llsd+json";
  935. // return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
  936. response.ContentType = "application/llsd+xml";
  937. return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
  938. }
  939. /// <summary>
  940. /// Checks if we have an Exact path in the LLSD handlers for the path provided
  941. /// </summary>
  942. /// <param name="path">URI of the request</param>
  943. /// <returns>true if we have one, false if not</returns>
  944. private bool DoWeHaveALLSDHandler(string path)
  945. {
  946. string[] pathbase = path.Split('/');
  947. string searchquery = "/";
  948. if (pathbase.Length < 1)
  949. return false;
  950. for (int i = 1; i < pathbase.Length; i++)
  951. {
  952. searchquery += pathbase[i];
  953. if (pathbase.Length - 1 != i)
  954. searchquery += "/";
  955. }
  956. string bestMatch = null;
  957. foreach (string pattern in m_llsdHandlers.Keys)
  958. {
  959. if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
  960. {
  961. bestMatch = pattern;
  962. }
  963. }
  964. // extra kicker to remove the default XMLRPC login case.. just in case..
  965. if (path != "/" && bestMatch == "/" && searchquery != "/")
  966. return false;
  967. if (path == "/")
  968. return false;
  969. if (String.IsNullOrEmpty(bestMatch))
  970. {
  971. return false;
  972. }
  973. else
  974. {
  975. return true;
  976. }
  977. }
  978. /// <summary>
  979. /// Checks if we have an Exact path in the HTTP handlers for the path provided
  980. /// </summary>
  981. /// <param name="path">URI of the request</param>
  982. /// <returns>true if we have one, false if not</returns>
  983. private bool DoWeHaveAHTTPHandler(string path)
  984. {
  985. string[] pathbase = path.Split('/');
  986. string searchquery = "/";
  987. if (pathbase.Length < 1)
  988. return false;
  989. for (int i = 1; i < pathbase.Length; i++)
  990. {
  991. searchquery += pathbase[i];
  992. if (pathbase.Length - 1 != i)
  993. searchquery += "/";
  994. }
  995. string bestMatch = null;
  996. //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery);
  997. lock (m_HTTPHandlers)
  998. {
  999. foreach (string pattern in m_HTTPHandlers.Keys)
  1000. {
  1001. if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
  1002. {
  1003. bestMatch = pattern;
  1004. }
  1005. }
  1006. // extra kicker to remove the default XMLRPC login case.. just in case..
  1007. if (path == "/")
  1008. return false;
  1009. if (String.IsNullOrEmpty(bestMatch))
  1010. {
  1011. return false;
  1012. }
  1013. else
  1014. {
  1015. return true;
  1016. }
  1017. }
  1018. }
  1019. private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler)
  1020. {
  1021. llsdHandler = null;
  1022. // Pull out the first part of the path
  1023. // splitting the path by '/' means we'll get the following return..
  1024. // {0}/{1}/{2}
  1025. // where {0} isn't something we really control 100%
  1026. string[] pathbase = path.Split('/');
  1027. string searchquery = "/";
  1028. if (pathbase.Length < 1)
  1029. return false;
  1030. for (int i=1; i<pathbase.Length; i++)
  1031. {
  1032. searchquery += pathbase[i];
  1033. if (pathbase.Length-1 != i)
  1034. searchquery += "/";
  1035. }
  1036. // while the matching algorithm below doesn't require it, we're expecting a query in the form
  1037. //
  1038. // [] = optional
  1039. // /resource/UUID/action[/action]
  1040. //
  1041. // now try to get the closest match to the reigstered path
  1042. // at least for OGP, registered path would probably only consist of the /resource/
  1043. string bestMatch = null;
  1044. foreach (string pattern in m_llsdHandlers.Keys)
  1045. {
  1046. if (searchquery.ToLower().StartsWith(pattern.ToLower()))
  1047. {
  1048. if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
  1049. {
  1050. // You have to specifically register for '/' and to get it, you must specificaly request it
  1051. //
  1052. if (pattern == "/" && searchquery == "/" || pattern != "/")
  1053. bestMatch = pattern;
  1054. }
  1055. }
  1056. }
  1057. if (String.IsNullOrEmpty(bestMatch))
  1058. {
  1059. llsdHandler = null;
  1060. return false;
  1061. }
  1062. else
  1063. {
  1064. llsdHandler = m_llsdHandlers[bestMatch];
  1065. return true;
  1066. }
  1067. }
  1068. private OSDMap GenerateNoLLSDHandlerResponse()
  1069. {
  1070. OSDMap map = new OSDMap();
  1071. map["reason"] = OSD.FromString("LLSDRequest");
  1072. map["message"] = OSD.FromString("No handler registered for LLSD Requests");
  1073. map["login"] = OSD.FromString("false");
  1074. return map;
  1075. }
  1076. /// <summary>
  1077. /// A specific agent handler was provided. Such a handler is expecetd to have an
  1078. /// intimate, and highly specific relationship with the client. Consequently,
  1079. /// nothing is done here.
  1080. /// </summary>
  1081. /// <param name="handler"></param>
  1082. /// <param name="request"></param>
  1083. /// <param name="response"></param>
  1084. private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response)
  1085. {
  1086. // In the case of REST, then handler is responsible for ALL aspects of
  1087. // the request/response handling. Nothing is done here, not even encoding.
  1088. try
  1089. {
  1090. return handler.Handle(request, response);
  1091. }
  1092. catch (Exception e)
  1093. {
  1094. // If the handler did in fact close the stream, then this will blow
  1095. // chunks. So that that doesn't disturb anybody we throw away any
  1096. // and all exceptions raised. We've done our best to release the
  1097. // client.
  1098. try
  1099. {
  1100. m_log.Warn("[HTTP-AGENT]: Error - " + e.Message);
  1101. response.SendChunked = false;
  1102. response.KeepAlive = true;
  1103. response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError;
  1104. //response.OutputStream.Close();
  1105. try
  1106. {
  1107. response.Send();
  1108. //response.FreeContext();
  1109. }
  1110. catch (SocketException f)
  1111. {
  1112. // This has to be here to prevent a Linux/Mono crash
  1113. m_log.WarnFormat(
  1114. "[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", f);
  1115. }
  1116. }
  1117. catch(Exception)
  1118. {
  1119. }
  1120. }
  1121. // Indicate that the request has been "handled"
  1122. return true;
  1123. }
  1124. public void HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
  1125. {
  1126. // m_log.DebugFormat(
  1127. // "[BASE HTTP SERVER]: HandleHTTPRequest for request to {0}, method {1}",
  1128. // request.RawUrl, request.HttpMethod);
  1129. switch (request.HttpMethod)
  1130. {
  1131. case "OPTIONS":
  1132. response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
  1133. return;
  1134. default:
  1135. HandleContentVerbs(request, response);
  1136. return;
  1137. }
  1138. }
  1139. private void HandleContentVerbs(OSHttpRequest request, OSHttpResponse response)
  1140. {
  1141. // m_log.DebugFormat("[BASE HTTP SERVER]: HandleContentVerbs for request to {0}", request.RawUrl);
  1142. // This is a test. There's a workable alternative.. as this way sucks.
  1143. // We'd like to put this into a text file parhaps that's easily editable.
  1144. //
  1145. // For this test to work, I used the following secondlife.exe parameters
  1146. // "C:\Program Files\SecondLifeWindLight\SecondLifeWindLight.exe" -settings settings_windlight.xml -channel "Second Life WindLight" -set SystemLanguage en-us -loginpage http://10.1.1.2:8002/?show_login_form=TRUE -loginuri http://10.1.1.2:8002 -user 10.1.1.2
  1147. //
  1148. // Even after all that, there's still an error, but it's a start.
  1149. //
  1150. // I depend on show_login_form being in the secondlife.exe parameters to figure out
  1151. // to display the form, or process it.
  1152. // a better way would be nifty.
  1153. Stream requestStream = request.InputStream;
  1154. Encoding encoding = Encoding.UTF8;
  1155. StreamReader reader = new StreamReader(requestStream, encoding);
  1156. string requestBody = reader.ReadToEnd();
  1157. // avoid warning for now
  1158. reader.ReadToEnd();
  1159. reader.Close();
  1160. requestStream.Close();
  1161. Hashtable keysvals = new Hashtable();
  1162. Hashtable headervals = new Hashtable();
  1163. Hashtable requestVars = new Hashtable();
  1164. string host = String.Empty;
  1165. string[] querystringkeys = request.QueryString.AllKeys;
  1166. string[] rHeaders = request.Headers.AllKeys;
  1167. keysvals.Add("body", requestBody);
  1168. keysvals.Add("uri", request.RawUrl);
  1169. keysvals.Add("content-type", request.ContentType);
  1170. keysvals.Add("http-method", request.HttpMethod);
  1171. foreach (string queryname in querystringkeys)
  1172. {
  1173. // m_log.DebugFormat(
  1174. // "[BASE HTTP SERVER]: Got query paremeter {0}={1}", queryname, request.QueryString[queryname]);
  1175. keysvals.Add(queryname, request.QueryString[queryname]);
  1176. requestVars.Add(queryname, keysvals[queryname]);
  1177. }
  1178. foreach (string headername in rHeaders)
  1179. {
  1180. // m_log.Debug("[BASE HTTP SERVER]: " + headername + "=" + request.Headers[headername]);
  1181. headervals[headername] = request.Headers[headername];
  1182. }
  1183. if (headervals.Contains("Host"))
  1184. {
  1185. host = (string)headervals["Host"];
  1186. }
  1187. keysvals.Add("headers", headervals);
  1188. keysvals.Add("querystringkeys", querystringkeys);
  1189. keysvals.Add("requestvars", requestVars);
  1190. // keysvals.Add("form", request.Form);
  1191. if (keysvals.Contains("method"))
  1192. {
  1193. // m_log.Debug("[BASE HTTP SERVER]: Contains Method");
  1194. string method = (string) keysvals["method"];
  1195. // m_log.Debug("[BASE HTTP SERVER]: " + requestBody);
  1196. GenericHTTPMethod requestprocessor;
  1197. bool foundHandler = TryGetHTTPHandler(method, out requestprocessor);
  1198. if (foundHandler)
  1199. {
  1200. Hashtable responsedata1 = requestprocessor(keysvals);
  1201. DoHTTPGruntWork(responsedata1,response);
  1202. //SendHTML500(response);
  1203. }
  1204. else
  1205. {
  1206. // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found");
  1207. SendHTML404(response, host);
  1208. }
  1209. }
  1210. else
  1211. {
  1212. GenericHTTPMethod requestprocessor;
  1213. bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor);
  1214. if (foundHandler)
  1215. {
  1216. Hashtable responsedata2 = requestprocessor(keysvals);
  1217. DoHTTPGruntWork(responsedata2, response);
  1218. //SendHTML500(response);
  1219. }
  1220. else
  1221. {
  1222. // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found2");
  1223. SendHTML404(response, host);
  1224. }
  1225. }
  1226. }
  1227. private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler)
  1228. {
  1229. httpHandler = null;
  1230. // Pull out the first part of the path
  1231. // splitting the path by '/' means we'll get the following return..
  1232. // {0}/{1}/{2}
  1233. // where {0} isn't something we really control 100%
  1234. string[] pathbase = path.Split('/');
  1235. string searchquery = "/";
  1236. if (pathbase.Length < 1)
  1237. return false;
  1238. for (int i = 1; i < pathbase.Length; i++)
  1239. {
  1240. searchquery += pathbase[i];
  1241. if (pathbase.Length - 1 != i)
  1242. searchquery += "/";
  1243. }
  1244. // while the matching algorithm below doesn't require it, we're expecting a query in the form
  1245. //
  1246. // [] = optional
  1247. // /resource/UUID/action[/action]
  1248. //
  1249. // now try to get the closest match to the reigstered path
  1250. // at least for OGP, registered path would probably only consist of the /resource/
  1251. string bestMatch = null;
  1252. // m_log.DebugFormat(
  1253. // "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery);
  1254. lock (m_HTTPHandlers)
  1255. {
  1256. foreach (string pattern in m_HTTPHandlers.Keys)
  1257. {
  1258. if (searchquery.ToLower().StartsWith(pattern.ToLower()))
  1259. {
  1260. if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
  1261. {
  1262. // You have to specifically register for '/' and to get it, you must specifically request it
  1263. if (pattern == "/" && searchquery == "/" || pattern != "/")
  1264. bestMatch = pattern;
  1265. }
  1266. }
  1267. }
  1268. if (String.IsNullOrEmpty(bestMatch))
  1269. {
  1270. httpHandler = null;
  1271. return false;
  1272. }
  1273. else
  1274. {
  1275. if (bestMatch == "/" && searchquery != "/")
  1276. return false;
  1277. httpHandler = m_HTTPHandlers[bestMatch];
  1278. return true;
  1279. }
  1280. }
  1281. }
  1282. internal void DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
  1283. {
  1284. //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
  1285. int responsecode = (int)responsedata["int_response_code"];
  1286. string responseString = (string)responsedata["str_response_string"];
  1287. string contentType = (string)responsedata["content_type"];
  1288. if (responsedata.ContainsKey("error_status_text"))
  1289. {
  1290. response.StatusDescription = (string)responsedata["error_status_text"];
  1291. }
  1292. if (responsedata.ContainsKey("http_protocol_version"))
  1293. {
  1294. response.ProtocolVersion = (string)responsedata["http_protocol_version"];
  1295. }
  1296. if (responsedata.ContainsKey("keepalive"))
  1297. {
  1298. bool keepalive = (bool)responsedata["keepalive"];
  1299. response.KeepAlive = keepalive;
  1300. }
  1301. if (responsedata.ContainsKey("reusecontext"))
  1302. response.ReuseContext = (bool) responsedata["reusecontext"];
  1303. // Cross-Origin Resource Sharing with simple requests
  1304. if (responsedata.ContainsKey("access_control_allow_origin"))
  1305. response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]);
  1306. //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
  1307. //and should check for NullReferenceExceptions
  1308. if (string.IsNullOrEmpty(contentType))
  1309. {
  1310. contentType = "text/html";
  1311. }
  1312. // The client ignores anything but 200 here for web login, so ensure that this is 200 for that
  1313. response.StatusCode = responsecode;
  1314. if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
  1315. {
  1316. response.RedirectLocation = (string)responsedata["str_redirect_location"];
  1317. response.StatusCode = responsecode;
  1318. }
  1319. response.AddHeader("Content-Type", contentType);
  1320. byte[] buffer;
  1321. if (!(contentType.Contains("image")
  1322. || contentType.Contains("x-shockwave-flash")
  1323. || contentType.Contains("application/x-oar")
  1324. || contentType.Contains("application/vnd.ll.mesh")))
  1325. {
  1326. // Text
  1327. buffer = Encoding.UTF8.GetBytes(responseString);
  1328. }
  1329. else
  1330. {
  1331. // Binary!
  1332. buffer = Convert.FromBase64String(responseString);
  1333. }
  1334. response.SendChunked = false;
  1335. response.ContentLength64 = buffer.Length;
  1336. response.ContentEncoding = Encoding.UTF8;
  1337. try
  1338. {
  1339. response.OutputStream.Write(buffer, 0, buffer.Length);
  1340. }
  1341. catch (Exception ex)
  1342. {
  1343. m_log.Warn("[HTTPD]: Error - " + ex.Message);
  1344. }
  1345. finally
  1346. {
  1347. //response.OutputStream.Close();
  1348. try
  1349. {
  1350. response.OutputStream.Flush();
  1351. response.Send();
  1352. //if (!response.KeepAlive && response.ReuseContext)
  1353. // response.FreeContext();
  1354. }
  1355. catch (SocketException e)
  1356. {
  1357. // This has to be here to prevent a Linux/Mono crash
  1358. m_log.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  1359. }
  1360. catch (IOException e)
  1361. {
  1362. m_log.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e.Message);
  1363. }
  1364. }
  1365. }
  1366. public void SendHTML404(OSHttpResponse response, string host)
  1367. {
  1368. // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
  1369. response.StatusCode = 404;
  1370. response.AddHeader("Content-type", "text/html");
  1371. string responseString = GetHTTP404(host);
  1372. byte[] buffer = Encoding.UTF8.GetBytes(responseString);
  1373. response.SendChunked = false;
  1374. response.ContentLength64 = buffer.Length;
  1375. response.ContentEncoding = Encoding.UTF8;
  1376. try
  1377. {
  1378. response.OutputStream.Write(buffer, 0, buffer.Length);
  1379. }
  1380. catch (Exception ex)
  1381. {
  1382. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  1383. }
  1384. finally
  1385. {
  1386. //response.OutputStream.Close();
  1387. try
  1388. {
  1389. response.Send();
  1390. //response.FreeContext();
  1391. }
  1392. catch (SocketException e)
  1393. {
  1394. // This has to be here to prevent a Linux/Mono crash
  1395. m_log.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  1396. }
  1397. }
  1398. }
  1399. public void SendHTML500(OSHttpResponse response)
  1400. {
  1401. // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
  1402. response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
  1403. response.AddHeader("Content-type", "text/html");
  1404. string responseString = GetHTTP500();
  1405. byte[] buffer = Encoding.UTF8.GetBytes(responseString);
  1406. response.SendChunked = false;
  1407. response.ContentLength64 = buffer.Length;
  1408. response.ContentEncoding = Encoding.UTF8;
  1409. try
  1410. {
  1411. response.OutputStream.Write(buffer, 0, buffer.Length);
  1412. }
  1413. catch (Exception ex)
  1414. {
  1415. m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
  1416. }
  1417. finally
  1418. {
  1419. //response.OutputStream.Close();
  1420. try
  1421. {
  1422. response.Send();
  1423. //response.FreeContext();
  1424. }
  1425. catch (SocketException e)
  1426. {
  1427. // This has to be here to prevent a Linux/Mono crash
  1428. m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e);
  1429. }
  1430. }
  1431. }
  1432. public void Start()
  1433. {
  1434. StartHTTP();
  1435. }
  1436. private void StartHTTP()
  1437. {
  1438. try
  1439. {
  1440. //m_httpListener = new HttpListener();
  1441. NotSocketErrors = 0;
  1442. if (!m_ssl)
  1443. {
  1444. //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
  1445. //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/");
  1446. m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port);
  1447. m_httpListener2.ExceptionThrown += httpServerException;
  1448. m_httpListener2.LogWriter = httpserverlog;
  1449. // Uncomment this line in addition to those in HttpServerLogWriter
  1450. // if you want more detailed trace information from the HttpServer
  1451. //m_httpListener2.UseTraceLogs = true;
  1452. //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor;
  1453. }
  1454. else
  1455. {
  1456. //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/");
  1457. //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
  1458. }
  1459. m_httpListener2.RequestReceived += OnRequest;
  1460. //m_httpListener.Start();
  1461. m_httpListener2.Start(64);
  1462. // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
  1463. m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000);
  1464. HTTPDRunning = true;
  1465. //HttpListenerContext context;
  1466. //while (true)
  1467. //{
  1468. // context = m_httpListener.GetContext();
  1469. // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context);
  1470. // }
  1471. }
  1472. catch (Exception e)
  1473. {
  1474. m_log.Error("[BASE HTTP SERVER]: Error - " + e.Message);
  1475. m_log.Error("[BASE HTTP SERVER]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?");
  1476. // We want this exception to halt the entire server since in current configurations we aren't too
  1477. // useful without inbound HTTP.
  1478. throw e;
  1479. }
  1480. }
  1481. public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
  1482. {
  1483. switch (err)
  1484. {
  1485. case SocketError.NotSocket:
  1486. NotSocketErrors++;
  1487. break;
  1488. }
  1489. }
  1490. public void httpServerException(object source, Exception exception)
  1491. {
  1492. m_log.ErrorFormat("[BASE HTTP SERVER]: {0} had an exception {1}", source.ToString(), exception.ToString());
  1493. /*
  1494. if (HTTPDRunning)// && NotSocketErrors > 5)
  1495. {
  1496. Stop();
  1497. Thread.Sleep(200);
  1498. StartHTTP();
  1499. m_log.Warn("[HTTPSERVER]: Died. Trying to kick.....");
  1500. }
  1501. */
  1502. }
  1503. public void Stop()
  1504. {
  1505. HTTPDRunning = false;
  1506. try
  1507. {
  1508. m_httpListener2.ExceptionThrown -= httpServerException;
  1509. //m_httpListener2.DisconnectHandler = null;
  1510. m_httpListener2.LogWriter = null;
  1511. m_httpListener2.RequestReceived -= OnRequest;
  1512. m_httpListener2.Stop();
  1513. }
  1514. catch (NullReferenceException)
  1515. {
  1516. m_log.Warn("[BASE HTTP SERVER]: Null Reference when stopping HttpServer.");
  1517. }
  1518. }
  1519. public void RemoveStreamHandler(string httpMethod, string path)
  1520. {
  1521. string handlerKey = GetHandlerKey(httpMethod, path);
  1522. //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey);
  1523. lock (m_streamHandlers) m_streamHandlers.Remove(handlerKey);
  1524. }
  1525. public void RemoveHTTPHandler(string httpMethod, string path)
  1526. {
  1527. lock (m_HTTPHandlers)
  1528. {
  1529. if (httpMethod != null && httpMethod.Length == 0)
  1530. {
  1531. m_HTTPHandlers.Remove(path);
  1532. return;
  1533. }
  1534. m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path));
  1535. }
  1536. }
  1537. public void RemovePollServiceHTTPHandler(string httpMethod, string path)
  1538. {
  1539. lock (m_pollHandlers)
  1540. {
  1541. if (m_pollHandlers.ContainsKey(httpMethod))
  1542. {
  1543. m_pollHandlers.Remove(httpMethod);
  1544. }
  1545. }
  1546. RemoveHTTPHandler(httpMethod, path);
  1547. }
  1548. public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
  1549. {
  1550. try
  1551. {
  1552. if (handler == m_agentHandlers[agent])
  1553. {
  1554. m_agentHandlers.Remove(agent);
  1555. return true;
  1556. }
  1557. }
  1558. catch(KeyNotFoundException)
  1559. {
  1560. }
  1561. return false;
  1562. }
  1563. public void RemoveXmlRPCHandler(string method)
  1564. {
  1565. lock (m_rpcHandlers)
  1566. {
  1567. if (m_rpcHandlers.ContainsKey(method))
  1568. {
  1569. m_rpcHandlers.Remove(method);
  1570. }
  1571. }
  1572. }
  1573. public bool RemoveLLSDHandler(string path, LLSDMethod handler)
  1574. {
  1575. try
  1576. {
  1577. if (handler == m_llsdHandlers[path])
  1578. {
  1579. m_llsdHandlers.Remove(path);
  1580. return true;
  1581. }
  1582. }
  1583. catch (KeyNotFoundException)
  1584. {
  1585. // This is an exception to prevent crashing because of invalid code
  1586. }
  1587. return false;
  1588. }
  1589. public string GetHTTP404(string host)
  1590. {
  1591. string file = Path.Combine(".", "http_404.html");
  1592. if (!File.Exists(file))
  1593. return getDefaultHTTP404(host);
  1594. StreamReader sr = File.OpenText(file);
  1595. string result = sr.ReadToEnd();
  1596. sr.Close();
  1597. return result;
  1598. }
  1599. public string GetHTTP500()
  1600. {
  1601. string file = Path.Combine(".", "http_500.html");
  1602. if (!File.Exists(file))
  1603. return getDefaultHTTP500();
  1604. StreamReader sr = File.OpenText(file);
  1605. string result = sr.ReadToEnd();
  1606. sr.Close();
  1607. return result;
  1608. }
  1609. // Fallback HTTP responses in case the HTTP error response files don't exist
  1610. private static string getDefaultHTTP404(string host)
  1611. {
  1612. return "<HTML><HEAD><TITLE>404 Page not found</TITLE><BODY><BR /><H1>Ooops!</H1><P>The page you requested has been obsconded with by knomes. Find hippos quick!</P><P>If you are trying to log-in, your link parameters should have: &quot;-loginpage http://" + host + "/?method=login -loginuri http://" + host + "/&quot; in your link </P></BODY></HTML>";
  1613. }
  1614. private static string getDefaultHTTP500()
  1615. {
  1616. return "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE><BODY><BR /><H1>Ooops!</H1><P>The server you requested is overun by knomes! Find hippos quick!</P></BODY></HTML>";
  1617. }
  1618. }
  1619. public class HttpServerContextObj
  1620. {
  1621. public IHttpClientContext context = null;
  1622. public IHttpRequest req = null;
  1623. public OSHttpRequest oreq = null;
  1624. public OSHttpResponse oresp = null;
  1625. public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs)
  1626. {
  1627. context = contxt;
  1628. req = reqs;
  1629. }
  1630. public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp)
  1631. {
  1632. oreq = osreq;
  1633. oresp = osresp;
  1634. }
  1635. }
  1636. /// <summary>
  1637. /// Relays HttpServer log messages to our own logging mechanism.
  1638. /// </summary>
  1639. /// To use this you must uncomment the switch section
  1640. ///
  1641. /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs
  1642. /// property in StartHttp() for the HttpListener
  1643. public class HttpServerLogWriter : ILogWriter
  1644. {
  1645. // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  1646. public void Write(object source, LogPrio priority, string message)
  1647. {
  1648. /*
  1649. switch (priority)
  1650. {
  1651. case LogPrio.Trace:
  1652. m_log.DebugFormat("[{0}]: {1}", source, message);
  1653. break;
  1654. case LogPrio.Debug:
  1655. m_log.DebugFormat("[{0}]: {1}", source, message);
  1656. break;
  1657. case LogPrio.Error:
  1658. m_log.ErrorFormat("[{0}]: {1}", source, message);
  1659. break;
  1660. case LogPrio.Info:
  1661. m_log.InfoFormat("[{0}]: {1}", source, message);
  1662. break;
  1663. case LogPrio.Warning:
  1664. m_log.WarnFormat("[{0}]: {1}", source, message);
  1665. break;
  1666. case LogPrio.Fatal:
  1667. m_log.ErrorFormat("[{0}]: FATAL! - {1}", source, message);
  1668. break;
  1669. default:
  1670. break;
  1671. }
  1672. */
  1673. return;
  1674. }
  1675. }
  1676. }