opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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  
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.IO;
33 using System.Net;
34 using System.Net.Sockets;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Reflection;
37 using System.Globalization;
38 using System.Text;
39 using System.Threading;
40 using System.Xml;
41 using HttpServer;
42 using log4net;
43 using Nwc.XmlRpc;
44 using OpenMetaverse.StructuredData;
45 using CoolHTTPListener = HttpServer.HttpListener;
46 using HttpListener=System.Net.HttpListener;
47 using LogPrio=HttpServer.LogPrio;
48 using OpenSim.Framework.Monitoring;
49  
50 namespace OpenSim.Framework.Servers.HttpServer
51 {
52 public class BaseHttpServer : IHttpServer
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
56  
57 /// <summary>
58 /// This is a pending websocket request before it got an sucessful upgrade response.
59 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to
60 /// start the connection and optionally provide an origin authentication method.
61 /// </summary>
62 /// <param name="servicepath"></param>
63 /// <param name="handler"></param>
64 public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler);
65  
66 /// <summary>
67 /// Gets or sets the debug level.
68 /// </summary>
69 /// <value>
70 /// See MainServer.DebugLevel.
71 /// </value>
72 public int DebugLevel { get; set; }
73  
74 /// <summary>
75 /// Request number for diagnostic purposes.
76 /// </summary>
77 /// <remarks>
78 /// This is an internal number. In some debug situations an external number may also be supplied in the
79 /// opensim-request-id header but we are not currently logging this.
80 /// </remarks>
81 public int RequestNumber { get; private set; }
82  
83 /// <summary>
84 /// Statistic for holding number of requests processed.
85 /// </summary>
86 private Stat m_requestsProcessedStat;
87  
88 private volatile int NotSocketErrors = 0;
89 public volatile bool HTTPDRunning = false;
90  
91 // protected HttpListener m_httpListener;
92 protected CoolHTTPListener m_httpListener2;
93 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
94 protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
95 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
96 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
97 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
98 protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
99 protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
100 // protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
101 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
102 new Dictionary<string, PollServiceEventArgs>();
103  
104 protected Dictionary<string, WebSocketRequestDelegate> m_WebSocketHandlers =
105 new Dictionary<string, WebSocketRequestDelegate>();
106  
107 protected uint m_port;
108 protected uint m_sslport;
109 protected bool m_ssl;
110 private X509Certificate2 m_cert;
111 protected bool m_firstcaps = true;
112 protected string m_SSLCommonName = "";
113  
114 protected IPAddress m_listenIPAddress = IPAddress.Any;
115  
116 private PollServiceRequestManager m_PollServiceManager;
117  
118 public uint SSLPort
119 {
120 get { return m_sslport; }
121 }
122  
123 public string SSLCommonName
124 {
125 get { return m_SSLCommonName; }
126 }
127  
128 public uint Port
129 {
130 get { return m_port; }
131 }
132  
133 public bool UseSSL
134 {
135 get { return m_ssl; }
136 }
137  
138 public IPAddress ListenIPAddress
139 {
140 get { return m_listenIPAddress; }
141 set { m_listenIPAddress = value; }
142 }
143  
144 public BaseHttpServer(uint port)
145 {
146 m_port = port;
147 }
148  
149 public BaseHttpServer(uint port, bool ssl) : this (port)
150 {
151 m_ssl = ssl;
152 }
153  
154 public BaseHttpServer(uint port, bool ssl, uint sslport, string CN) : this (port, ssl)
155 {
156 if (m_ssl)
157 {
158 m_sslport = sslport;
159 }
160 }
161  
162 public BaseHttpServer(uint port, bool ssl, string CPath, string CPass) : this (port, ssl)
163 {
164 if (m_ssl)
165 {
166 m_cert = new X509Certificate2(CPath, CPass);
167 }
168 }
169  
170 /// <summary>
171 /// Add a stream handler to the http server. If the handler already exists, then nothing happens.
172 /// </summary>
173 /// <param name="handler"></param>
174 public void AddStreamHandler(IRequestHandler handler)
175 {
176 string httpMethod = handler.HttpMethod;
177 string path = handler.Path;
178 string handlerKey = GetHandlerKey(httpMethod, path);
179  
180 lock (m_streamHandlers)
181 {
182 if (!m_streamHandlers.ContainsKey(handlerKey))
183 {
184 // m_log.DebugFormat("[BASE HTTP SERVER]: Adding handler key {0}", handlerKey);
185 m_streamHandlers.Add(handlerKey, handler);
186 }
187 }
188 }
189  
190 public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler)
191 {
192 lock (m_WebSocketHandlers)
193 {
194 if (!m_WebSocketHandlers.ContainsKey(servicepath))
195 m_WebSocketHandlers.Add(servicepath, handler);
196 }
197 }
198  
199 public void RemoveWebSocketHandler(string servicepath)
200 {
201 lock (m_WebSocketHandlers)
202 if (m_WebSocketHandlers.ContainsKey(servicepath))
203 m_WebSocketHandlers.Remove(servicepath);
204 }
205  
206 public List<string> GetStreamHandlerKeys()
207 {
208 lock (m_streamHandlers)
209 return new List<string>(m_streamHandlers.Keys);
210 }
211  
212 private static string GetHandlerKey(string httpMethod, string path)
213 {
214 return httpMethod + ":" + path;
215 }
216  
217 public bool AddXmlRPCHandler(string method, XmlRpcMethod handler)
218 {
219 return AddXmlRPCHandler(method, handler, true);
220 }
221  
222 public bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive)
223 {
224 lock (m_rpcHandlers)
225 {
226 m_rpcHandlers[method] = handler;
227 m_rpcHandlersKeepAlive[method] = keepAlive; // default
228 }
229  
230 return true;
231 }
232  
233 public XmlRpcMethod GetXmlRPCHandler(string method)
234 {
235 lock (m_rpcHandlers)
236 {
237 if (m_rpcHandlers.ContainsKey(method))
238 {
239 return m_rpcHandlers[method];
240 }
241 else
242 {
243 return null;
244 }
245 }
246 }
247  
248 public List<string> GetXmlRpcHandlerKeys()
249 {
250 lock (m_rpcHandlers)
251 return new List<string>(m_rpcHandlers.Keys);
252 }
253  
254 // JsonRPC
255 public bool AddJsonRPCHandler(string method, JsonRPCMethod handler)
256 {
257 lock(jsonRpcHandlers)
258 {
259 jsonRpcHandlers.Add(method, handler);
260 }
261 return true;
262 }
263  
264 public JsonRPCMethod GetJsonRPCHandler(string method)
265 {
266 lock (jsonRpcHandlers)
267 {
268 if (jsonRpcHandlers.ContainsKey(method))
269 {
270 return jsonRpcHandlers[method];
271 }
272 else
273 {
274 return null;
275 }
276 }
277 }
278  
279 public List<string> GetJsonRpcHandlerKeys()
280 {
281 lock (jsonRpcHandlers)
282 return new List<string>(jsonRpcHandlers.Keys);
283 }
284  
285 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
286 {
287 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
288  
289 lock (m_HTTPHandlers)
290 {
291 if (!m_HTTPHandlers.ContainsKey(methodName))
292 {
293 m_HTTPHandlers.Add(methodName, handler);
294 return true;
295 }
296 }
297  
298 //must already have a handler for that path so return false
299 return false;
300 }
301  
302 public List<string> GetHTTPHandlerKeys()
303 {
304 lock (m_HTTPHandlers)
305 return new List<string>(m_HTTPHandlers.Keys);
306 }
307  
308 public bool AddPollServiceHTTPHandler(string methodName, PollServiceEventArgs args)
309 {
310 lock (m_pollHandlers)
311 {
312 if (!m_pollHandlers.ContainsKey(methodName))
313 {
314 m_pollHandlers.Add(methodName, args);
315 return true;
316 }
317 }
318  
319 return false;
320 }
321  
322 public List<string> GetPollServiceHandlerKeys()
323 {
324 lock (m_pollHandlers)
325 return new List<string>(m_pollHandlers.Keys);
326 }
327  
328 // // Note that the agent string is provided simply to differentiate
329 // // the handlers - it is NOT required to be an actual agent header
330 // // value.
331 // public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
332 // {
333 // lock (m_agentHandlers)
334 // {
335 // if (!m_agentHandlers.ContainsKey(agent))
336 // {
337 // m_agentHandlers.Add(agent, handler);
338 // return true;
339 // }
340 // }
341 //
342 // //must already have a handler for that path so return false
343 // return false;
344 // }
345 //
346 // public List<string> GetAgentHandlerKeys()
347 // {
348 // lock (m_agentHandlers)
349 // return new List<string>(m_agentHandlers.Keys);
350 // }
351  
352 public bool AddLLSDHandler(string path, LLSDMethod handler)
353 {
354 lock (m_llsdHandlers)
355 {
356 if (!m_llsdHandlers.ContainsKey(path))
357 {
358 m_llsdHandlers.Add(path, handler);
359 return true;
360 }
361 }
362 return false;
363 }
364  
365 public List<string> GetLLSDHandlerKeys()
366 {
367 lock (m_llsdHandlers)
368 return new List<string>(m_llsdHandlers.Keys);
369 }
370  
371 public bool SetDefaultLLSDHandler(DefaultLLSDMethod handler)
372 {
373 m_defaultLlsdHandler = handler;
374 return true;
375 }
376  
377 private void OnRequest(object source, RequestEventArgs args)
378 {
379 RequestNumber++;
380  
381 try
382 {
383 IHttpClientContext context = (IHttpClientContext)source;
384 IHttpRequest request = args.Request;
385  
386 PollServiceEventArgs psEvArgs;
387  
388 if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs))
389 {
390 psEvArgs.RequestsReceived++;
391  
392 PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request);
393  
394 if (psEvArgs.Request != null)
395 {
396 OSHttpRequest req = new OSHttpRequest(context, request);
397  
398 Stream requestStream = req.InputStream;
399  
400 Encoding encoding = Encoding.UTF8;
401 StreamReader reader = new StreamReader(requestStream, encoding);
402  
403 string requestBody = reader.ReadToEnd();
404  
405 Hashtable keysvals = new Hashtable();
406 Hashtable headervals = new Hashtable();
407  
408 string[] querystringkeys = req.QueryString.AllKeys;
409 string[] rHeaders = req.Headers.AllKeys;
410  
411 keysvals.Add("body", requestBody);
412 keysvals.Add("uri", req.RawUrl);
413 keysvals.Add("content-type", req.ContentType);
414 keysvals.Add("http-method", req.HttpMethod);
415  
416 foreach (string queryname in querystringkeys)
417 {
418 keysvals.Add(queryname, req.QueryString[queryname]);
419 }
420  
421 foreach (string headername in rHeaders)
422 {
423 headervals[headername] = req.Headers[headername];
424 }
425  
426 keysvals.Add("headers", headervals);
427 keysvals.Add("querystringkeys", querystringkeys);
428  
429 psEvArgs.Request(psreq.RequestID, keysvals);
430 }
431  
432 m_PollServiceManager.Enqueue(psreq);
433 }
434 else
435 {
436 OnHandleRequestIOThread(context, request);
437 }
438 }
439 catch (Exception e)
440 {
441 m_log.Error(String.Format("[BASE HTTP SERVER]: OnRequest() failed: {0} ", e.Message), e);
442 }
443 }
444  
445 private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
446 {
447 OSHttpRequest req = new OSHttpRequest(context, request);
448 WebSocketRequestDelegate dWebSocketRequestDelegate = null;
449 lock (m_WebSocketHandlers)
450 {
451 if (m_WebSocketHandlers.ContainsKey(req.RawUrl))
452 dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl];
453 }
454 if (dWebSocketRequestDelegate != null)
455 {
456 dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192));
457 return;
458 }
459  
460 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
461 resp.ReuseContext = true;
462 HandleRequest(req, resp);
463  
464 // !!!HACK ALERT!!!
465 // There seems to be a bug in the underlying http code that makes subsequent requests
466 // come up with trash in Accept headers. Until that gets fixed, we're cleaning them up here.
467 if (request.AcceptTypes != null)
468 for (int i = 0; i < request.AcceptTypes.Length; i++)
469 request.AcceptTypes[i] = string.Empty;
470 }
471  
472 // public void ConvertIHttpClientContextToOSHttp(object stateinfo)
473 // {
474 // HttpServerContextObj objstate = (HttpServerContextObj)stateinfo;
475  
476 // OSHttpRequest request = objstate.oreq;
477 // OSHttpResponse resp = objstate.oresp;
478  
479 // HandleRequest(request,resp);
480 // }
481  
482 /// <summary>
483 /// This methods is the start of incoming HTTP request handling.
484 /// </summary>
485 /// <param name="request"></param>
486 /// <param name="response"></param>
487 public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response)
488 {
489 if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread
490 {
491 try
492 {
493 byte[] buffer500 = SendHTML500(response);
494 response.Body.Write(buffer500,0,buffer500.Length);
495 response.Body.Close();
496 }
497 catch
498 {
499 }
500  
501 return;
502 }
503  
504 string requestMethod = request.HttpMethod;
505 string uriString = request.RawUrl;
506  
507 int requestStartTick = Environment.TickCount;
508  
509 // Will be adjusted later on.
510 int requestEndTick = requestStartTick;
511  
512 IRequestHandler requestHandler = null;
513  
514 try
515 {
516 // OpenSim.Framework.WebUtil.OSHeaderRequestID
517 // if (request.Headers["opensim-request-id"] != null)
518 // reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
519 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
520  
521 Culture.SetCurrentCulture();
522  
523 // // This is the REST agent interface. We require an agent to properly identify
524 // // itself. If the REST handler recognizes the prefix it will attempt to
525 // // satisfy the request. If it is not recognizable, and no damage has occurred
526 // // the request can be passed through to the other handlers. This is a low
527 // // probability event; if a request is matched it is normally expected to be
528 // // handled
529 // IHttpAgentHandler agentHandler;
530 //
531 // if (TryGetAgentHandler(request, response, out agentHandler))
532 // {
533 // if (HandleAgentRequest(agentHandler, request, response))
534 // {
535 // requestEndTick = Environment.TickCount;
536 // return;
537 // }
538 // }
539  
540 //response.KeepAlive = true;
541 response.SendChunked = false;
542  
543 string path = request.RawUrl;
544 string handlerKey = GetHandlerKey(request.HttpMethod, path);
545 byte[] buffer = null;
546  
547 if (TryGetStreamHandler(handlerKey, out requestHandler))
548 {
549 if (DebugLevel >= 3)
550 LogIncomingToStreamHandler(request, requestHandler);
551  
552 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
553  
554 if (requestHandler is IStreamedRequestHandler)
555 {
556 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
557  
558 buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response);
559 }
560 else if (requestHandler is IGenericHTTPHandler)
561 {
562 //m_log.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler");
563 IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler;
564 Stream requestStream = request.InputStream;
565  
566 Encoding encoding = Encoding.UTF8;
567 StreamReader reader = new StreamReader(requestStream, encoding);
568  
569 string requestBody = reader.ReadToEnd();
570  
571 reader.Close();
572 //requestStream.Close();
573  
574 Hashtable keysvals = new Hashtable();
575 Hashtable headervals = new Hashtable();
576 //string host = String.Empty;
577  
578 string[] querystringkeys = request.QueryString.AllKeys;
579 string[] rHeaders = request.Headers.AllKeys;
580  
581 foreach (string queryname in querystringkeys)
582 {
583 keysvals.Add(queryname, request.QueryString[queryname]);
584 }
585  
586 foreach (string headername in rHeaders)
587 {
588 //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]);
589 headervals[headername] = request.Headers[headername];
590 }
591  
592 // if (headervals.Contains("Host"))
593 // {
594 // host = (string)headervals["Host"];
595 // }
596  
597 keysvals.Add("requestbody", requestBody);
598 keysvals.Add("headers",headervals);
599 if (keysvals.Contains("method"))
600 {
601 //m_log.Warn("[HTTP]: Contains Method");
602 //string method = (string)keysvals["method"];
603 //m_log.Warn("[HTTP]: " + requestBody);
604  
605 }
606  
607 buffer = DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response);
608 }
609 else
610 {
611 IStreamHandler streamHandler = (IStreamHandler)requestHandler;
612  
613 using (MemoryStream memoryStream = new MemoryStream())
614 {
615 streamHandler.Handle(path, request.InputStream, memoryStream, request, response);
616 memoryStream.Flush();
617 buffer = memoryStream.ToArray();
618 }
619 }
620 }
621 else
622 {
623 switch (request.ContentType)
624 {
625 case null:
626 case "text/html":
627 if (DebugLevel >= 3)
628 LogIncomingToContentTypeHandler(request);
629  
630 buffer = HandleHTTPRequest(request, response);
631 break;
632  
633 case "application/llsd+xml":
634 case "application/xml+llsd":
635 case "application/llsd+json":
636 if (DebugLevel >= 3)
637 LogIncomingToContentTypeHandler(request);
638  
639 buffer = HandleLLSDRequests(request, response);
640 break;
641  
642 case "application/json-rpc":
643 if (DebugLevel >= 3)
644 LogIncomingToContentTypeHandler(request);
645  
646 buffer = HandleJsonRpcRequests(request, response);
647 break;
648  
649 case "text/xml":
650 case "application/xml":
651 case "application/json":
652  
653 default:
654 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
655 // Point of note.. the DoWeHaveA methods check for an EXACT path
656 // if (request.RawUrl.Contains("/CAPS/EQG"))
657 // {
658 // int i = 1;
659 // }
660 //m_log.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler");
661 if (DoWeHaveALLSDHandler(request.RawUrl))
662 {
663 if (DebugLevel >= 3)
664 LogIncomingToContentTypeHandler(request);
665  
666 buffer = HandleLLSDRequests(request, response);
667 }
668 // m_log.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl);
669 else if (DoWeHaveAHTTPHandler(request.RawUrl))
670 {
671 if (DebugLevel >= 3)
672 LogIncomingToContentTypeHandler(request);
673  
674 buffer = HandleHTTPRequest(request, response);
675 }
676 else
677 {
678 if (DebugLevel >= 3)
679 LogIncomingToXmlRpcHandler(request);
680  
681 // generic login request.
682 buffer = HandleXmlRpcRequests(request, response);
683 }
684  
685 break;
686 }
687 }
688  
689 request.InputStream.Close();
690  
691 if (buffer != null)
692 {
693 if (!response.SendChunked && response.ContentLength64 <= 0)
694 response.ContentLength64 = buffer.LongLength;
695  
696 response.OutputStream.Write(buffer, 0, buffer.Length);
697 }
698  
699 // Do not include the time taken to actually send the response to the caller in the measurement
700 // time. This is to avoid logging when it's the client that is slow to process rather than the
701 // server
702 requestEndTick = Environment.TickCount;
703  
704 response.Send();
705  
706 //response.OutputStream.Close();
707  
708 //response.FreeContext();
709 }
710 catch (SocketException e)
711 {
712 // At least on linux, it appears that if the client makes a request without requiring the response,
713 // an unconnected socket exception is thrown when we close the response output stream. There's no
714 // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore
715 // the exception instead.
716 //
717 // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go
718 // with the minimum first
719 m_log.Warn(String.Format("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux ", e.Message), e);
720 }
721 catch (IOException e)
722 {
723 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
724 }
725 catch (Exception e)
726 {
727 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
728 try
729 {
730 byte[] buffer500 = SendHTML500(response);
731 response.Body.Write(buffer500, 0, buffer500.Length);
732 response.Body.Close();
733 }
734 catch
735 {
736 }
737 }
738 finally
739 {
740 // Every month or so this will wrap and give bad numbers, not really a problem
741 // since its just for reporting
742 int tickdiff = requestEndTick - requestStartTick;
743 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
744 {
745 m_log.InfoFormat(
746 "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
747 RequestNumber,
748 requestMethod,
749 uriString,
750 requestHandler != null ? requestHandler.Name : "",
751 requestHandler != null ? requestHandler.Description : "",
752 request.RemoteIPEndPoint,
753 tickdiff);
754 }
755 else if (DebugLevel >= 4)
756 {
757 m_log.DebugFormat(
758 "[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms",
759 RequestNumber,
760 Port,
761 tickdiff);
762 }
763 }
764 }
765  
766 private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
767 {
768 m_log.DebugFormat(
769 "[BASE HTTP SERVER]: HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}",
770 RequestNumber,
771 Port,
772 request.HttpMethod,
773 request.Url.PathAndQuery,
774 requestHandler.Name,
775 requestHandler.Description,
776 request.RemoteIPEndPoint);
777  
778 if (DebugLevel >= 5)
779 LogIncomingInDetail(request);
780 }
781  
782 private void LogIncomingToContentTypeHandler(OSHttpRequest request)
783 {
784 m_log.DebugFormat(
785 "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
786 RequestNumber,
787 Port,
788 string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType,
789 request.HttpMethod,
790 request.Url.PathAndQuery,
791 request.RemoteIPEndPoint);
792  
793 if (DebugLevel >= 5)
794 LogIncomingInDetail(request);
795 }
796  
797 private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
798 {
799 m_log.DebugFormat(
800 "[BASE HTTP SERVER]: HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}",
801 RequestNumber,
802 Port,
803 request.HttpMethod,
804 request.Url.PathAndQuery,
805 request.RemoteIPEndPoint);
806  
807 if (DebugLevel >= 5)
808 LogIncomingInDetail(request);
809 }
810  
811 private void LogIncomingInDetail(OSHttpRequest request)
812 {
813 using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8))
814 {
815 string output;
816  
817 if (DebugLevel == 5)
818 {
819 const int sampleLength = 80;
820 char[] sampleChars = new char[sampleLength + 3];
821 reader.Read(sampleChars, 0, sampleLength);
822 sampleChars[80] = '.';
823 sampleChars[81] = '.';
824 sampleChars[82] = '.';
825 output = new string(sampleChars);
826 }
827 else
828 {
829 output = reader.ReadToEnd();
830 }
831  
832 m_log.DebugFormat("[BASE HTTP SERVER]: {0}", output.Replace("\n", @"\n"));
833 }
834 }
835  
836 private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler)
837 {
838 string bestMatch = null;
839  
840 lock (m_streamHandlers)
841 {
842 foreach (string pattern in m_streamHandlers.Keys)
843 {
844 if (handlerKey.StartsWith(pattern))
845 {
846 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
847 {
848 bestMatch = pattern;
849 }
850 }
851 }
852  
853 if (String.IsNullOrEmpty(bestMatch))
854 {
855 streamHandler = null;
856 return false;
857 }
858 else
859 {
860 streamHandler = m_streamHandlers[bestMatch];
861 return true;
862 }
863 }
864 }
865  
866 private bool TryGetPollServiceHTTPHandler(string handlerKey, out PollServiceEventArgs oServiceEventArgs)
867 {
868 string bestMatch = null;
869  
870 lock (m_pollHandlers)
871 {
872 foreach (string pattern in m_pollHandlers.Keys)
873 {
874 if (handlerKey.StartsWith(pattern))
875 {
876 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
877 {
878 bestMatch = pattern;
879 }
880 }
881 }
882  
883 if (String.IsNullOrEmpty(bestMatch))
884 {
885 oServiceEventArgs = null;
886 return false;
887 }
888 else
889 {
890 oServiceEventArgs = m_pollHandlers[bestMatch];
891 return true;
892 }
893 }
894 }
895  
896 private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler)
897 {
898 // m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey);
899  
900 string bestMatch = null;
901  
902 lock (m_HTTPHandlers)
903 {
904 foreach (string pattern in m_HTTPHandlers.Keys)
905 {
906 if (handlerKey.StartsWith(pattern))
907 {
908 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
909 {
910 bestMatch = pattern;
911 }
912 }
913 }
914  
915 if (String.IsNullOrEmpty(bestMatch))
916 {
917 HTTPHandler = null;
918 return false;
919 }
920 else
921 {
922 HTTPHandler = m_HTTPHandlers[bestMatch];
923 return true;
924 }
925 }
926 }
927  
928 // private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
929 // {
930 // agentHandler = null;
931 //
932 // lock (m_agentHandlers)
933 // {
934 // foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
935 // {
936 // if (handler.Match(request, response))
937 // {
938 // agentHandler = handler;
939 // return true;
940 // }
941 // }
942 // }
943 //
944 // return false;
945 // }
946  
947 /// <summary>
948 /// Try all the registered xmlrpc handlers when an xmlrpc request is received.
949 /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method.
950 /// </summary>
951 /// <param name="request"></param>
952 /// <param name="response"></param>
953 private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response)
954 {
955 Stream requestStream = request.InputStream;
956  
957 Encoding encoding = Encoding.UTF8;
958 StreamReader reader = new StreamReader(requestStream, encoding);
959  
960 string requestBody = reader.ReadToEnd();
961 reader.Close();
962 requestStream.Close();
963 //m_log.Debug(requestBody);
964 requestBody = requestBody.Replace("<base64></base64>", "");
965 string responseString = String.Empty;
966 XmlRpcRequest xmlRprcRequest = null;
967  
968 try
969 {
970 xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody);
971 }
972 catch (XmlException e)
973 {
974 if (DebugLevel >= 1)
975 {
976 if (DebugLevel >= 2)
977 m_log.Warn(
978 string.Format(
979 "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}. XML was '{1}'. Sending blank response. Exception ",
980 request.RemoteIPEndPoint, requestBody),
981 e);
982 else
983 {
984 m_log.WarnFormat(
985 "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}, length {1}. Sending blank response.",
986 request.RemoteIPEndPoint, requestBody.Length);
987 }
988 }
989 }
990  
991 if (xmlRprcRequest != null)
992 {
993 string methodName = xmlRprcRequest.MethodName;
994 if (methodName != null)
995 {
996 xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1]
997 XmlRpcResponse xmlRpcResponse;
998  
999 XmlRpcMethod method;
1000 bool methodWasFound;
1001 bool keepAlive = false;
1002 lock (m_rpcHandlers)
1003 {
1004 methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method);
1005 if (methodWasFound)
1006 keepAlive = m_rpcHandlersKeepAlive[methodName];
1007 }
1008  
1009 if (methodWasFound)
1010 {
1011 xmlRprcRequest.Params.Add(request.Url); // Param[2]
1012  
1013 string xff = "X-Forwarded-For";
1014 string xfflower = xff.ToLower();
1015 foreach (string s in request.Headers.AllKeys)
1016 {
1017 if (s != null && s.Equals(xfflower))
1018 {
1019 xff = xfflower;
1020 break;
1021 }
1022 }
1023 xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3]
1024  
1025 try
1026 {
1027 xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint);
1028 }
1029 catch(Exception e)
1030 {
1031 string errorMessage
1032 = String.Format(
1033 "Requested method [{0}] from {1} threw exception: {2} {3}",
1034 methodName, request.RemoteIPEndPoint.Address, e.Message, e.StackTrace);
1035  
1036 m_log.ErrorFormat("[BASE HTTP SERVER]: {0}", errorMessage);
1037  
1038 // if the registered XmlRpc method threw an exception, we pass a fault-code along
1039 xmlRpcResponse = new XmlRpcResponse();
1040  
1041 // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
1042 xmlRpcResponse.SetFault(-32603, errorMessage);
1043 }
1044  
1045 // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here
1046 response.KeepAlive = keepAlive;
1047 }
1048 else
1049 {
1050 xmlRpcResponse = new XmlRpcResponse();
1051  
1052 // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
1053 xmlRpcResponse.SetFault(
1054 XmlRpcErrorCodes.SERVER_ERROR_METHOD,
1055 String.Format("Requested method [{0}] not found", methodName));
1056 }
1057  
1058 response.ContentType = "text/xml";
1059 using (MemoryStream outs = new MemoryStream())
1060 {
1061 using (XmlTextWriter writer = new XmlTextWriter(outs, Encoding.UTF8))
1062 {
1063 writer.Formatting = Formatting.None;
1064 XmlRpcResponseSerializer.Singleton.Serialize(writer, xmlRpcResponse);
1065 writer.Flush();
1066 outs.Flush();
1067 outs.Position = 0;
1068 using (StreamReader sr = new StreamReader(outs))
1069 {
1070 responseString = sr.ReadToEnd();
1071 }
1072 }
1073 }
1074 }
1075 else
1076 {
1077 //HandleLLSDRequests(request, response);
1078 response.ContentType = "text/plain";
1079 response.StatusCode = 404;
1080 response.StatusDescription = "Not Found";
1081 response.ProtocolVersion = "HTTP/1.0";
1082 responseString = "Not found";
1083 response.KeepAlive = false;
1084  
1085 m_log.ErrorFormat(
1086 "[BASE HTTP SERVER]: Handler not found for http request {0} {1}",
1087 request.HttpMethod, request.Url.PathAndQuery);
1088 }
1089 }
1090  
1091 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1092  
1093 response.SendChunked = false;
1094 response.ContentLength64 = buffer.Length;
1095 response.ContentEncoding = Encoding.UTF8;
1096  
1097 return buffer;
1098 }
1099  
1100 // JsonRpc (v2.0 only)
1101 // Batch requests not yet supported
1102 private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
1103 {
1104 Stream requestStream = request.InputStream;
1105 JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
1106 OSDMap jsonRpcRequest = null;
1107  
1108 try
1109 {
1110 jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
1111 }
1112 catch (LitJson.JsonException e)
1113 {
1114 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1115 jsonRpcResponse.Error.Message = e.Message;
1116 }
1117  
1118 requestStream.Close();
1119  
1120 if (jsonRpcRequest != null)
1121 {
1122 if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
1123 {
1124 jsonRpcResponse.JsonRpc = "2.0";
1125  
1126 // If we have no id, then it's a "notification"
1127 if (jsonRpcRequest.ContainsKey("id"))
1128 {
1129 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1130 }
1131  
1132 string methodname = jsonRpcRequest["method"];
1133 JsonRPCMethod method;
1134  
1135 if (jsonRpcHandlers.ContainsKey(methodname))
1136 {
1137 lock(jsonRpcHandlers)
1138 {
1139 jsonRpcHandlers.TryGetValue(methodname, out method);
1140 }
1141 bool res = false;
1142 try
1143 {
1144 res = method(jsonRpcRequest, ref jsonRpcResponse);
1145 if(!res)
1146 {
1147 // The handler sent back an unspecified error
1148 if(jsonRpcResponse.Error.Code == 0)
1149 {
1150 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1151 }
1152 }
1153 }
1154 catch (Exception e)
1155 {
1156 string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
1157 m_log.Error(ErrorMessage);
1158 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1159 jsonRpcResponse.Error.Message = ErrorMessage;
1160 }
1161 }
1162 else // Error no hanlder defined for requested method
1163 {
1164 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1165 jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
1166 }
1167 }
1168 else // not json-rpc 2.0 could be v1
1169 {
1170 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1171 jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
1172  
1173 if (jsonRpcRequest.ContainsKey("id"))
1174 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1175 }
1176 }
1177  
1178 response.KeepAlive = true;
1179 string responseData = string.Empty;
1180  
1181 responseData = jsonRpcResponse.Serialize();
1182  
1183 byte[] buffer = Encoding.UTF8.GetBytes(responseData);
1184 return buffer;
1185 }
1186  
1187 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
1188 {
1189 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
1190 Stream requestStream = request.InputStream;
1191  
1192 Encoding encoding = Encoding.UTF8;
1193 StreamReader reader = new StreamReader(requestStream, encoding);
1194  
1195 string requestBody = reader.ReadToEnd();
1196 reader.Close();
1197 requestStream.Close();
1198  
1199 //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody);
1200 response.KeepAlive = true;
1201  
1202 OSD llsdRequest = null;
1203 OSD llsdResponse = null;
1204  
1205 bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest"));
1206  
1207 if (requestBody.Length == 0)
1208 // Get Request
1209 {
1210 requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>";
1211 }
1212 try
1213 {
1214 llsdRequest = OSDParser.Deserialize(requestBody);
1215 }
1216 catch (Exception ex)
1217 {
1218 m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
1219 }
1220  
1221 if (llsdRequest != null)// && m_defaultLlsdHandler != null)
1222 {
1223 LLSDMethod llsdhandler = null;
1224  
1225 if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV)
1226 {
1227 // we found a registered llsd handler to service this request
1228 llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString());
1229 }
1230 else
1231 {
1232 // we didn't find a registered llsd handler to service this request
1233 // check if we have a default llsd handler
1234  
1235 if (m_defaultLlsdHandler != null)
1236 {
1237 // LibOMV path
1238 llsdResponse = m_defaultLlsdHandler(llsdRequest, request.RemoteIPEndPoint);
1239 }
1240 else
1241 {
1242 // Oops, no handler for this.. give em the failed message
1243 llsdResponse = GenerateNoLLSDHandlerResponse();
1244 }
1245 }
1246 }
1247 else
1248 {
1249 llsdResponse = GenerateNoLLSDHandlerResponse();
1250 }
1251  
1252 byte[] buffer = new byte[0];
1253  
1254 if (llsdResponse.ToString() == "shutdown404!")
1255 {
1256 response.ContentType = "text/plain";
1257 response.StatusCode = 404;
1258 response.StatusDescription = "Not Found";
1259 response.ProtocolVersion = "HTTP/1.0";
1260 buffer = Encoding.UTF8.GetBytes("Not found");
1261 }
1262 else
1263 {
1264 // Select an appropriate response format
1265 buffer = BuildLLSDResponse(request, response, llsdResponse);
1266 }
1267  
1268 response.SendChunked = false;
1269 response.ContentLength64 = buffer.Length;
1270 response.ContentEncoding = Encoding.UTF8;
1271 response.KeepAlive = true;
1272  
1273 return buffer;
1274 }
1275  
1276 private byte[] BuildLLSDResponse(OSHttpRequest request, OSHttpResponse response, OSD llsdResponse)
1277 {
1278 if (request.AcceptTypes != null && request.AcceptTypes.Length > 0)
1279 {
1280 foreach (string strAccept in request.AcceptTypes)
1281 {
1282 switch (strAccept)
1283 {
1284 case "application/llsd+xml":
1285 case "application/xml":
1286 case "text/xml":
1287 response.ContentType = strAccept;
1288 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1289 case "application/llsd+json":
1290 case "application/json":
1291 response.ContentType = strAccept;
1292 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1293 }
1294 }
1295 }
1296  
1297 if (!String.IsNullOrEmpty(request.ContentType))
1298 {
1299 switch (request.ContentType)
1300 {
1301 case "application/llsd+xml":
1302 case "application/xml":
1303 case "text/xml":
1304 response.ContentType = request.ContentType;
1305 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1306 case "application/llsd+json":
1307 case "application/json":
1308 response.ContentType = request.ContentType;
1309 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1310 }
1311 }
1312  
1313 // response.ContentType = "application/llsd+json";
1314 // return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1315 response.ContentType = "application/llsd+xml";
1316 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1317 }
1318  
1319 /// <summary>
1320 /// Checks if we have an Exact path in the LLSD handlers for the path provided
1321 /// </summary>
1322 /// <param name="path">URI of the request</param>
1323 /// <returns>true if we have one, false if not</returns>
1324 private bool DoWeHaveALLSDHandler(string path)
1325 {
1326 string[] pathbase = path.Split('/');
1327 string searchquery = "/";
1328  
1329 if (pathbase.Length < 1)
1330 return false;
1331  
1332 for (int i = 1; i < pathbase.Length; i++)
1333 {
1334 searchquery += pathbase[i];
1335 if (pathbase.Length - 1 != i)
1336 searchquery += "/";
1337 }
1338  
1339 string bestMatch = null;
1340  
1341 lock (m_llsdHandlers)
1342 {
1343 foreach (string pattern in m_llsdHandlers.Keys)
1344 {
1345 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1346 bestMatch = pattern;
1347 }
1348 }
1349  
1350 // extra kicker to remove the default XMLRPC login case.. just in case..
1351 if (path != "/" && bestMatch == "/" && searchquery != "/")
1352 return false;
1353  
1354 if (path == "/")
1355 return false;
1356  
1357 if (String.IsNullOrEmpty(bestMatch))
1358 {
1359 return false;
1360 }
1361 else
1362 {
1363 return true;
1364 }
1365 }
1366  
1367 /// <summary>
1368 /// Checks if we have an Exact path in the HTTP handlers for the path provided
1369 /// </summary>
1370 /// <param name="path">URI of the request</param>
1371 /// <returns>true if we have one, false if not</returns>
1372 private bool DoWeHaveAHTTPHandler(string path)
1373 {
1374 string[] pathbase = path.Split('/');
1375 string searchquery = "/";
1376  
1377 if (pathbase.Length < 1)
1378 return false;
1379  
1380 for (int i = 1; i < pathbase.Length; i++)
1381 {
1382 searchquery += pathbase[i];
1383 if (pathbase.Length - 1 != i)
1384 searchquery += "/";
1385 }
1386  
1387 string bestMatch = null;
1388  
1389 //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery);
1390  
1391 lock (m_HTTPHandlers)
1392 {
1393 foreach (string pattern in m_HTTPHandlers.Keys)
1394 {
1395 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1396 {
1397 bestMatch = pattern;
1398 }
1399 }
1400  
1401 // extra kicker to remove the default XMLRPC login case.. just in case..
1402 if (path == "/")
1403 return false;
1404  
1405 if (String.IsNullOrEmpty(bestMatch))
1406 {
1407 return false;
1408 }
1409 else
1410 {
1411 return true;
1412 }
1413 }
1414 }
1415  
1416 private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler)
1417 {
1418 llsdHandler = null;
1419 // Pull out the first part of the path
1420 // splitting the path by '/' means we'll get the following return..
1421 // {0}/{1}/{2}
1422 // where {0} isn't something we really control 100%
1423  
1424 string[] pathbase = path.Split('/');
1425 string searchquery = "/";
1426  
1427 if (pathbase.Length < 1)
1428 return false;
1429  
1430 for (int i=1; i<pathbase.Length; i++)
1431 {
1432 searchquery += pathbase[i];
1433 if (pathbase.Length-1 != i)
1434 searchquery += "/";
1435 }
1436  
1437 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1438 //
1439 // [] = optional
1440 // /resource/UUID/action[/action]
1441 //
1442 // now try to get the closest match to the reigstered path
1443 // at least for OGP, registered path would probably only consist of the /resource/
1444  
1445 string bestMatch = null;
1446  
1447 lock (m_llsdHandlers)
1448 {
1449 foreach (string pattern in m_llsdHandlers.Keys)
1450 {
1451 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1452 {
1453 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1454 {
1455 // You have to specifically register for '/' and to get it, you must specificaly request it
1456 //
1457 if (pattern == "/" && searchquery == "/" || pattern != "/")
1458 bestMatch = pattern;
1459 }
1460 }
1461 }
1462  
1463 if (String.IsNullOrEmpty(bestMatch))
1464 {
1465 llsdHandler = null;
1466 return false;
1467 }
1468 else
1469 {
1470 llsdHandler = m_llsdHandlers[bestMatch];
1471 return true;
1472 }
1473 }
1474 }
1475  
1476 private OSDMap GenerateNoLLSDHandlerResponse()
1477 {
1478 OSDMap map = new OSDMap();
1479 map["reason"] = OSD.FromString("LLSDRequest");
1480 map["message"] = OSD.FromString("No handler registered for LLSD Requests");
1481 map["login"] = OSD.FromString("false");
1482 return map;
1483 }
1484  
1485 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
1486 {
1487 // m_log.DebugFormat(
1488 // "[BASE HTTP SERVER]: HandleHTTPRequest for request to {0}, method {1}",
1489 // request.RawUrl, request.HttpMethod);
1490  
1491 switch (request.HttpMethod)
1492 {
1493 case "OPTIONS":
1494 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1495 return null;
1496  
1497 default:
1498 return HandleContentVerbs(request, response);
1499 }
1500 }
1501  
1502 private byte[] HandleContentVerbs(OSHttpRequest request, OSHttpResponse response)
1503 {
1504 // m_log.DebugFormat("[BASE HTTP SERVER]: HandleContentVerbs for request to {0}", request.RawUrl);
1505  
1506 // This is a test. There's a workable alternative.. as this way sucks.
1507 // We'd like to put this into a text file parhaps that's easily editable.
1508 //
1509 // For this test to work, I used the following secondlife.exe parameters
1510 // "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
1511 //
1512 // Even after all that, there's still an error, but it's a start.
1513 //
1514 // I depend on show_login_form being in the secondlife.exe parameters to figure out
1515 // to display the form, or process it.
1516 // a better way would be nifty.
1517  
1518 byte[] buffer;
1519  
1520 Stream requestStream = request.InputStream;
1521  
1522 Encoding encoding = Encoding.UTF8;
1523 StreamReader reader = new StreamReader(requestStream, encoding);
1524  
1525 string requestBody = reader.ReadToEnd();
1526 // avoid warning for now
1527 reader.ReadToEnd();
1528 reader.Close();
1529 requestStream.Close();
1530  
1531 Hashtable keysvals = new Hashtable();
1532 Hashtable headervals = new Hashtable();
1533  
1534 Hashtable requestVars = new Hashtable();
1535  
1536 string host = String.Empty;
1537  
1538 string[] querystringkeys = request.QueryString.AllKeys;
1539 string[] rHeaders = request.Headers.AllKeys;
1540  
1541 keysvals.Add("body", requestBody);
1542 keysvals.Add("uri", request.RawUrl);
1543 keysvals.Add("content-type", request.ContentType);
1544 keysvals.Add("http-method", request.HttpMethod);
1545  
1546 foreach (string queryname in querystringkeys)
1547 {
1548 // m_log.DebugFormat(
1549 // "[BASE HTTP SERVER]: Got query paremeter {0}={1}", queryname, request.QueryString[queryname]);
1550 keysvals.Add(queryname, request.QueryString[queryname]);
1551 requestVars.Add(queryname, keysvals[queryname]);
1552 }
1553  
1554 foreach (string headername in rHeaders)
1555 {
1556 // m_log.Debug("[BASE HTTP SERVER]: " + headername + "=" + request.Headers[headername]);
1557 headervals[headername] = request.Headers[headername];
1558 }
1559  
1560 if (headervals.Contains("Host"))
1561 {
1562 host = (string)headervals["Host"];
1563 }
1564  
1565 keysvals.Add("headers", headervals);
1566 keysvals.Add("querystringkeys", querystringkeys);
1567 keysvals.Add("requestvars", requestVars);
1568 // keysvals.Add("form", request.Form);
1569  
1570 if (keysvals.Contains("method"))
1571 {
1572 // m_log.Debug("[BASE HTTP SERVER]: Contains Method");
1573 string method = (string) keysvals["method"];
1574 // m_log.Debug("[BASE HTTP SERVER]: " + requestBody);
1575 GenericHTTPMethod requestprocessor;
1576 bool foundHandler = TryGetHTTPHandler(method, out requestprocessor);
1577 if (foundHandler)
1578 {
1579 Hashtable responsedata1 = requestprocessor(keysvals);
1580 buffer = DoHTTPGruntWork(responsedata1,response);
1581  
1582 //SendHTML500(response);
1583 }
1584 else
1585 {
1586 // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found");
1587 buffer = SendHTML404(response, host);
1588 }
1589 }
1590 else
1591 {
1592 GenericHTTPMethod requestprocessor;
1593 bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor);
1594 if (foundHandler)
1595 {
1596 Hashtable responsedata2 = requestprocessor(keysvals);
1597 buffer = DoHTTPGruntWork(responsedata2, response);
1598  
1599 //SendHTML500(response);
1600 }
1601 else
1602 {
1603 // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found2");
1604 buffer = SendHTML404(response, host);
1605 }
1606 }
1607  
1608 return buffer;
1609 }
1610  
1611 private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler)
1612 {
1613 httpHandler = null;
1614 // Pull out the first part of the path
1615 // splitting the path by '/' means we'll get the following return..
1616 // {0}/{1}/{2}
1617 // where {0} isn't something we really control 100%
1618  
1619 string[] pathbase = path.Split('/');
1620 string searchquery = "/";
1621  
1622 if (pathbase.Length < 1)
1623 return false;
1624  
1625 for (int i = 1; i < pathbase.Length; i++)
1626 {
1627 searchquery += pathbase[i];
1628 if (pathbase.Length - 1 != i)
1629 searchquery += "/";
1630 }
1631  
1632 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1633 //
1634 // [] = optional
1635 // /resource/UUID/action[/action]
1636 //
1637 // now try to get the closest match to the reigstered path
1638 // at least for OGP, registered path would probably only consist of the /resource/
1639  
1640 string bestMatch = null;
1641  
1642 // m_log.DebugFormat(
1643 // "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery);
1644  
1645 lock (m_HTTPHandlers)
1646 {
1647 foreach (string pattern in m_HTTPHandlers.Keys)
1648 {
1649 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1650 {
1651 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1652 {
1653 // You have to specifically register for '/' and to get it, you must specifically request it
1654 if (pattern == "/" && searchquery == "/" || pattern != "/")
1655 bestMatch = pattern;
1656 }
1657 }
1658 }
1659  
1660 if (String.IsNullOrEmpty(bestMatch))
1661 {
1662 httpHandler = null;
1663 return false;
1664 }
1665 else
1666 {
1667 if (bestMatch == "/" && searchquery != "/")
1668 return false;
1669  
1670 httpHandler = m_HTTPHandlers[bestMatch];
1671 return true;
1672 }
1673 }
1674 }
1675  
1676 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
1677 {
1678 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
1679 int responsecode = (int)responsedata["int_response_code"];
1680 string responseString = (string)responsedata["str_response_string"];
1681 string contentType = (string)responsedata["content_type"];
1682  
1683 if (responsedata.ContainsKey("error_status_text"))
1684 {
1685 response.StatusDescription = (string)responsedata["error_status_text"];
1686 }
1687 if (responsedata.ContainsKey("http_protocol_version"))
1688 {
1689 response.ProtocolVersion = (string)responsedata["http_protocol_version"];
1690 }
1691  
1692 if (responsedata.ContainsKey("keepalive"))
1693 {
1694 bool keepalive = (bool)responsedata["keepalive"];
1695 response.KeepAlive = keepalive;
1696  
1697 }
1698  
1699 if (responsedata.ContainsKey("reusecontext"))
1700 response.ReuseContext = (bool) responsedata["reusecontext"];
1701  
1702 // Cross-Origin Resource Sharing with simple requests
1703 if (responsedata.ContainsKey("access_control_allow_origin"))
1704 response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]);
1705  
1706 //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
1707 //and should check for NullReferenceExceptions
1708  
1709 if (string.IsNullOrEmpty(contentType))
1710 {
1711 contentType = "text/html";
1712 }
1713  
1714 // The client ignores anything but 200 here for web login, so ensure that this is 200 for that
1715  
1716 response.StatusCode = responsecode;
1717  
1718 if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
1719 {
1720 response.RedirectLocation = (string)responsedata["str_redirect_location"];
1721 response.StatusCode = responsecode;
1722 }
1723  
1724 response.AddHeader("Content-Type", contentType);
1725  
1726 byte[] buffer;
1727  
1728 if (!(contentType.Contains("image")
1729 || contentType.Contains("x-shockwave-flash")
1730 || contentType.Contains("application/x-oar")
1731 || contentType.Contains("application/vnd.ll.mesh")))
1732 {
1733 // Text
1734 buffer = Encoding.UTF8.GetBytes(responseString);
1735 }
1736 else
1737 {
1738 // Binary!
1739 buffer = Convert.FromBase64String(responseString);
1740 }
1741  
1742 response.SendChunked = false;
1743 response.ContentLength64 = buffer.Length;
1744 response.ContentEncoding = Encoding.UTF8;
1745  
1746 return buffer;
1747 }
1748  
1749 public byte[] SendHTML404(OSHttpResponse response, string host)
1750 {
1751 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1752 response.StatusCode = 404;
1753 response.AddHeader("Content-type", "text/html");
1754  
1755 string responseString = GetHTTP404(host);
1756 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1757  
1758 response.SendChunked = false;
1759 response.ContentLength64 = buffer.Length;
1760 response.ContentEncoding = Encoding.UTF8;
1761  
1762 return buffer;
1763 }
1764  
1765 public byte[] SendHTML500(OSHttpResponse response)
1766 {
1767 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1768 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1769 response.AddHeader("Content-type", "text/html");
1770  
1771 string responseString = GetHTTP500();
1772 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1773  
1774 response.SendChunked = false;
1775 response.ContentLength64 = buffer.Length;
1776 response.ContentEncoding = Encoding.UTF8;
1777  
1778  
1779 return buffer;
1780 }
1781  
1782 public void Start()
1783 {
1784 StartHTTP();
1785 }
1786  
1787 private void StartHTTP()
1788 {
1789 m_log.InfoFormat(
1790 "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port);
1791  
1792 try
1793 {
1794 //m_httpListener = new HttpListener();
1795  
1796 NotSocketErrors = 0;
1797 if (!m_ssl)
1798 {
1799 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1800 //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/");
1801 m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port);
1802 m_httpListener2.ExceptionThrown += httpServerException;
1803 m_httpListener2.LogWriter = httpserverlog;
1804  
1805 // Uncomment this line in addition to those in HttpServerLogWriter
1806 // if you want more detailed trace information from the HttpServer
1807 //m_httpListener2.UseTraceLogs = true;
1808  
1809 //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor;
1810 }
1811 else
1812 {
1813 //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/");
1814 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1815 m_httpListener2 = CoolHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert);
1816 m_httpListener2.ExceptionThrown += httpServerException;
1817 m_httpListener2.LogWriter = httpserverlog;
1818 }
1819  
1820 m_httpListener2.RequestReceived += OnRequest;
1821 //m_httpListener.Start();
1822 m_httpListener2.Start(64);
1823  
1824 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
1825 m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000);
1826 m_PollServiceManager.Start();
1827 HTTPDRunning = true;
1828  
1829 //HttpListenerContext context;
1830 //while (true)
1831 //{
1832 // context = m_httpListener.GetContext();
1833 // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context);
1834 // }
1835 }
1836 catch (Exception e)
1837 {
1838 m_log.Error("[BASE HTTP SERVER]: Error - " + e.Message);
1839 m_log.Error("[BASE HTTP SERVER]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?");
1840  
1841 // We want this exception to halt the entire server since in current configurations we aren't too
1842 // useful without inbound HTTP.
1843 throw e;
1844 }
1845  
1846 m_requestsProcessedStat
1847 = new Stat(
1848 "HTTPRequestsServed",
1849 "Number of inbound HTTP requests processed",
1850 "",
1851 "requests",
1852 "httpserver",
1853 Port.ToString(),
1854 StatType.Pull,
1855 MeasuresOfInterest.AverageChangeOverTime,
1856 stat => stat.Value = RequestNumber,
1857 StatVerbosity.Debug);
1858  
1859 StatsManager.RegisterStat(m_requestsProcessedStat);
1860 }
1861  
1862 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
1863 {
1864 switch (err)
1865 {
1866 case SocketError.NotSocket:
1867 NotSocketErrors++;
1868  
1869 break;
1870 }
1871 }
1872  
1873 public void httpServerException(object source, Exception exception)
1874 {
1875 m_log.Error(String.Format("[BASE HTTP SERVER]: {0} had an exception: {1} ", source.ToString(), exception.Message), exception);
1876 /*
1877 if (HTTPDRunning)// && NotSocketErrors > 5)
1878 {
1879 Stop();
1880 Thread.Sleep(200);
1881 StartHTTP();
1882 m_log.Warn("[HTTPSERVER]: Died. Trying to kick.....");
1883 }
1884 */
1885 }
1886  
1887 public void Stop()
1888 {
1889 HTTPDRunning = false;
1890  
1891 StatsManager.DeregisterStat(m_requestsProcessedStat);
1892  
1893 try
1894 {
1895 m_PollServiceManager.Stop();
1896  
1897 m_httpListener2.ExceptionThrown -= httpServerException;
1898 //m_httpListener2.DisconnectHandler = null;
1899  
1900 m_httpListener2.LogWriter = null;
1901 m_httpListener2.RequestReceived -= OnRequest;
1902 m_httpListener2.Stop();
1903 }
1904 catch (NullReferenceException)
1905 {
1906 m_log.Warn("[BASE HTTP SERVER]: Null Reference when stopping HttpServer.");
1907 }
1908 }
1909  
1910 public void RemoveStreamHandler(string httpMethod, string path)
1911 {
1912 string handlerKey = GetHandlerKey(httpMethod, path);
1913  
1914 //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey);
1915  
1916 lock (m_streamHandlers)
1917 m_streamHandlers.Remove(handlerKey);
1918 }
1919  
1920 public void RemoveHTTPHandler(string httpMethod, string path)
1921 {
1922 lock (m_HTTPHandlers)
1923 {
1924 if (httpMethod != null && httpMethod.Length == 0)
1925 {
1926 m_HTTPHandlers.Remove(path);
1927 return;
1928 }
1929  
1930 m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path));
1931 }
1932 }
1933  
1934 public void RemovePollServiceHTTPHandler(string httpMethod, string path)
1935 {
1936 lock (m_pollHandlers)
1937 m_pollHandlers.Remove(path);
1938 }
1939  
1940 // public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
1941 // {
1942 // lock (m_agentHandlers)
1943 // {
1944 // IHttpAgentHandler foundHandler;
1945 //
1946 // if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
1947 // {
1948 // m_agentHandlers.Remove(agent);
1949 // return true;
1950 // }
1951 // }
1952 //
1953 // return false;
1954 // }
1955  
1956 public void RemoveXmlRPCHandler(string method)
1957 {
1958 lock (m_rpcHandlers)
1959 m_rpcHandlers.Remove(method);
1960 }
1961  
1962 public void RemoveJsonRPCHandler(string method)
1963 {
1964 lock(jsonRpcHandlers)
1965 jsonRpcHandlers.Remove(method);
1966 }
1967  
1968 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
1969 {
1970 lock (m_llsdHandlers)
1971 {
1972 LLSDMethod foundHandler;
1973  
1974 if (m_llsdHandlers.TryGetValue(path, out foundHandler) && foundHandler == handler)
1975 {
1976 m_llsdHandlers.Remove(path);
1977 return true;
1978 }
1979 }
1980  
1981 return false;
1982 }
1983  
1984 public string GetHTTP404(string host)
1985 {
1986 string file = Path.Combine(".", "http_404.html");
1987 if (!File.Exists(file))
1988 return getDefaultHTTP404(host);
1989  
1990 StreamReader sr = File.OpenText(file);
1991 string result = sr.ReadToEnd();
1992 sr.Close();
1993 return result;
1994 }
1995  
1996 public string GetHTTP500()
1997 {
1998 string file = Path.Combine(".", "http_500.html");
1999 if (!File.Exists(file))
2000 return getDefaultHTTP500();
2001  
2002 StreamReader sr = File.OpenText(file);
2003 string result = sr.ReadToEnd();
2004 sr.Close();
2005 return result;
2006 }
2007  
2008 // Fallback HTTP responses in case the HTTP error response files don't exist
2009 private static string getDefaultHTTP404(string host)
2010 {
2011 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>";
2012 }
2013  
2014 private static string getDefaultHTTP500()
2015 {
2016 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>";
2017 }
2018 }
2019  
2020 public class HttpServerContextObj
2021 {
2022 public IHttpClientContext context = null;
2023 public IHttpRequest req = null;
2024 public OSHttpRequest oreq = null;
2025 public OSHttpResponse oresp = null;
2026  
2027 public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs)
2028 {
2029 context = contxt;
2030 req = reqs;
2031 }
2032  
2033 public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp)
2034 {
2035 oreq = osreq;
2036 oresp = osresp;
2037 }
2038 }
2039  
2040 /// <summary>
2041 /// Relays HttpServer log messages to our own logging mechanism.
2042 /// </summary>
2043 /// To use this you must uncomment the switch section
2044 ///
2045 /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs
2046 /// property in StartHttp() for the HttpListener
2047 public class HttpServerLogWriter : ILogWriter
2048 {
2049 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
2050  
2051 public void Write(object source, LogPrio priority, string message)
2052 {
2053 /*
2054 switch (priority)
2055 {
2056 case LogPrio.Trace:
2057 m_log.DebugFormat("[{0}]: {1}", source, message);
2058 break;
2059 case LogPrio.Debug:
2060 m_log.DebugFormat("[{0}]: {1}", source, message);
2061 break;
2062 case LogPrio.Error:
2063 m_log.ErrorFormat("[{0}]: {1}", source, message);
2064 break;
2065 case LogPrio.Info:
2066 m_log.InfoFormat("[{0}]: {1}", source, message);
2067 break;
2068 case LogPrio.Warning:
2069 m_log.WarnFormat("[{0}]: {1}", source, message);
2070 break;
2071 case LogPrio.Fatal:
2072 m_log.ErrorFormat("[{0}]: FATAL! - {1}", source, message);
2073 break;
2074 default:
2075 break;
2076 }
2077 */
2078  
2079 return;
2080 }
2081 }
2082 }