clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 (request.ContentType == null || 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 responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse);
1060 }
1061 else
1062 {
1063 //HandleLLSDRequests(request, response);
1064 response.ContentType = "text/plain";
1065 response.StatusCode = 404;
1066 response.StatusDescription = "Not Found";
1067 response.ProtocolVersion = "HTTP/1.0";
1068 responseString = "Not found";
1069 response.KeepAlive = false;
1070  
1071 m_log.ErrorFormat(
1072 "[BASE HTTP SERVER]: Handler not found for http request {0} {1}",
1073 request.HttpMethod, request.Url.PathAndQuery);
1074 }
1075 }
1076  
1077 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1078  
1079 response.SendChunked = false;
1080 response.ContentLength64 = buffer.Length;
1081 response.ContentEncoding = Encoding.UTF8;
1082  
1083 return buffer;
1084 }
1085  
1086 // JsonRpc (v2.0 only)
1087 // Batch requests not yet supported
1088 private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
1089 {
1090 Stream requestStream = request.InputStream;
1091 JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
1092 OSDMap jsonRpcRequest = null;
1093  
1094 try
1095 {
1096 jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
1097 }
1098 catch (LitJson.JsonException e)
1099 {
1100 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1101 jsonRpcResponse.Error.Message = e.Message;
1102 }
1103  
1104 requestStream.Close();
1105  
1106 if (jsonRpcRequest != null)
1107 {
1108 if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
1109 {
1110 jsonRpcResponse.JsonRpc = "2.0";
1111  
1112 // If we have no id, then it's a "notification"
1113 if (jsonRpcRequest.ContainsKey("id"))
1114 {
1115 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1116 }
1117  
1118 string methodname = jsonRpcRequest["method"];
1119 JsonRPCMethod method;
1120  
1121 if (jsonRpcHandlers.ContainsKey(methodname))
1122 {
1123 lock(jsonRpcHandlers)
1124 {
1125 jsonRpcHandlers.TryGetValue(methodname, out method);
1126 }
1127 bool res = false;
1128 try
1129 {
1130 res = method(jsonRpcRequest, ref jsonRpcResponse);
1131 if(!res)
1132 {
1133 // The handler sent back an unspecified error
1134 if(jsonRpcResponse.Error.Code == 0)
1135 {
1136 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1137 }
1138 }
1139 }
1140 catch (Exception e)
1141 {
1142 string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
1143 m_log.Error(ErrorMessage);
1144 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1145 jsonRpcResponse.Error.Message = ErrorMessage;
1146 }
1147 }
1148 else // Error no hanlder defined for requested method
1149 {
1150 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1151 jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
1152 }
1153 }
1154 else // not json-rpc 2.0 could be v1
1155 {
1156 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1157 jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
1158  
1159 if (jsonRpcRequest.ContainsKey("id"))
1160 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1161 }
1162 }
1163  
1164 response.KeepAlive = true;
1165 string responseData = string.Empty;
1166  
1167 responseData = jsonRpcResponse.Serialize();
1168  
1169 byte[] buffer = Encoding.UTF8.GetBytes(responseData);
1170 return buffer;
1171 }
1172  
1173 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
1174 {
1175 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
1176 Stream requestStream = request.InputStream;
1177  
1178 Encoding encoding = Encoding.UTF8;
1179 StreamReader reader = new StreamReader(requestStream, encoding);
1180  
1181 string requestBody = reader.ReadToEnd();
1182 reader.Close();
1183 requestStream.Close();
1184  
1185 //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody);
1186 response.KeepAlive = true;
1187  
1188 OSD llsdRequest = null;
1189 OSD llsdResponse = null;
1190  
1191 bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest"));
1192  
1193 if (requestBody.Length == 0)
1194 // Get Request
1195 {
1196 requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>";
1197 }
1198 try
1199 {
1200 llsdRequest = OSDParser.Deserialize(requestBody);
1201 }
1202 catch (Exception ex)
1203 {
1204 m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
1205 }
1206  
1207 if (llsdRequest != null)// && m_defaultLlsdHandler != null)
1208 {
1209 LLSDMethod llsdhandler = null;
1210  
1211 if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV)
1212 {
1213 // we found a registered llsd handler to service this request
1214 llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString());
1215 }
1216 else
1217 {
1218 // we didn't find a registered llsd handler to service this request
1219 // check if we have a default llsd handler
1220  
1221 if (m_defaultLlsdHandler != null)
1222 {
1223 // LibOMV path
1224 llsdResponse = m_defaultLlsdHandler(llsdRequest, request.RemoteIPEndPoint);
1225 }
1226 else
1227 {
1228 // Oops, no handler for this.. give em the failed message
1229 llsdResponse = GenerateNoLLSDHandlerResponse();
1230 }
1231 }
1232 }
1233 else
1234 {
1235 llsdResponse = GenerateNoLLSDHandlerResponse();
1236 }
1237  
1238 byte[] buffer = new byte[0];
1239  
1240 if (llsdResponse.ToString() == "shutdown404!")
1241 {
1242 response.ContentType = "text/plain";
1243 response.StatusCode = 404;
1244 response.StatusDescription = "Not Found";
1245 response.ProtocolVersion = "HTTP/1.0";
1246 buffer = Encoding.UTF8.GetBytes("Not found");
1247 }
1248 else
1249 {
1250 // Select an appropriate response format
1251 buffer = BuildLLSDResponse(request, response, llsdResponse);
1252 }
1253  
1254 response.SendChunked = false;
1255 response.ContentLength64 = buffer.Length;
1256 response.ContentEncoding = Encoding.UTF8;
1257 response.KeepAlive = true;
1258  
1259 return buffer;
1260 }
1261  
1262 private byte[] BuildLLSDResponse(OSHttpRequest request, OSHttpResponse response, OSD llsdResponse)
1263 {
1264 if (request.AcceptTypes != null && request.AcceptTypes.Length > 0)
1265 {
1266 foreach (string strAccept in request.AcceptTypes)
1267 {
1268 switch (strAccept)
1269 {
1270 case "application/llsd+xml":
1271 case "application/xml":
1272 case "text/xml":
1273 response.ContentType = strAccept;
1274 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1275 case "application/llsd+json":
1276 case "application/json":
1277 response.ContentType = strAccept;
1278 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1279 }
1280 }
1281 }
1282  
1283 if (!String.IsNullOrEmpty(request.ContentType))
1284 {
1285 switch (request.ContentType)
1286 {
1287 case "application/llsd+xml":
1288 case "application/xml":
1289 case "text/xml":
1290 response.ContentType = request.ContentType;
1291 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1292 case "application/llsd+json":
1293 case "application/json":
1294 response.ContentType = request.ContentType;
1295 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1296 }
1297 }
1298  
1299 // response.ContentType = "application/llsd+json";
1300 // return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1301 response.ContentType = "application/llsd+xml";
1302 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1303 }
1304  
1305 /// <summary>
1306 /// Checks if we have an Exact path in the LLSD handlers for the path provided
1307 /// </summary>
1308 /// <param name="path">URI of the request</param>
1309 /// <returns>true if we have one, false if not</returns>
1310 private bool DoWeHaveALLSDHandler(string path)
1311 {
1312 string[] pathbase = path.Split('/');
1313 string searchquery = "/";
1314  
1315 if (pathbase.Length < 1)
1316 return false;
1317  
1318 for (int i = 1; i < pathbase.Length; i++)
1319 {
1320 searchquery += pathbase[i];
1321 if (pathbase.Length - 1 != i)
1322 searchquery += "/";
1323 }
1324  
1325 string bestMatch = null;
1326  
1327 lock (m_llsdHandlers)
1328 {
1329 foreach (string pattern in m_llsdHandlers.Keys)
1330 {
1331 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1332 bestMatch = pattern;
1333 }
1334 }
1335  
1336 // extra kicker to remove the default XMLRPC login case.. just in case..
1337 if (path != "/" && bestMatch == "/" && searchquery != "/")
1338 return false;
1339  
1340 if (path == "/")
1341 return false;
1342  
1343 if (String.IsNullOrEmpty(bestMatch))
1344 {
1345 return false;
1346 }
1347 else
1348 {
1349 return true;
1350 }
1351 }
1352  
1353 /// <summary>
1354 /// Checks if we have an Exact path in the HTTP handlers for the path provided
1355 /// </summary>
1356 /// <param name="path">URI of the request</param>
1357 /// <returns>true if we have one, false if not</returns>
1358 private bool DoWeHaveAHTTPHandler(string path)
1359 {
1360 string[] pathbase = path.Split('/');
1361 string searchquery = "/";
1362  
1363 if (pathbase.Length < 1)
1364 return false;
1365  
1366 for (int i = 1; i < pathbase.Length; i++)
1367 {
1368 searchquery += pathbase[i];
1369 if (pathbase.Length - 1 != i)
1370 searchquery += "/";
1371 }
1372  
1373 string bestMatch = null;
1374  
1375 //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery);
1376  
1377 lock (m_HTTPHandlers)
1378 {
1379 foreach (string pattern in m_HTTPHandlers.Keys)
1380 {
1381 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1382 {
1383 bestMatch = pattern;
1384 }
1385 }
1386  
1387 // extra kicker to remove the default XMLRPC login case.. just in case..
1388 if (path == "/")
1389 return false;
1390  
1391 if (String.IsNullOrEmpty(bestMatch))
1392 {
1393 return false;
1394 }
1395 else
1396 {
1397 return true;
1398 }
1399 }
1400 }
1401  
1402 private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler)
1403 {
1404 llsdHandler = null;
1405 // Pull out the first part of the path
1406 // splitting the path by '/' means we'll get the following return..
1407 // {0}/{1}/{2}
1408 // where {0} isn't something we really control 100%
1409  
1410 string[] pathbase = path.Split('/');
1411 string searchquery = "/";
1412  
1413 if (pathbase.Length < 1)
1414 return false;
1415  
1416 for (int i=1; i<pathbase.Length; i++)
1417 {
1418 searchquery += pathbase[i];
1419 if (pathbase.Length-1 != i)
1420 searchquery += "/";
1421 }
1422  
1423 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1424 //
1425 // [] = optional
1426 // /resource/UUID/action[/action]
1427 //
1428 // now try to get the closest match to the reigstered path
1429 // at least for OGP, registered path would probably only consist of the /resource/
1430  
1431 string bestMatch = null;
1432  
1433 lock (m_llsdHandlers)
1434 {
1435 foreach (string pattern in m_llsdHandlers.Keys)
1436 {
1437 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1438 {
1439 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1440 {
1441 // You have to specifically register for '/' and to get it, you must specificaly request it
1442 //
1443 if (pattern == "/" && searchquery == "/" || pattern != "/")
1444 bestMatch = pattern;
1445 }
1446 }
1447 }
1448  
1449 if (String.IsNullOrEmpty(bestMatch))
1450 {
1451 llsdHandler = null;
1452 return false;
1453 }
1454 else
1455 {
1456 llsdHandler = m_llsdHandlers[bestMatch];
1457 return true;
1458 }
1459 }
1460 }
1461  
1462 private OSDMap GenerateNoLLSDHandlerResponse()
1463 {
1464 OSDMap map = new OSDMap();
1465 map["reason"] = OSD.FromString("LLSDRequest");
1466 map["message"] = OSD.FromString("No handler registered for LLSD Requests");
1467 map["login"] = OSD.FromString("false");
1468 return map;
1469 }
1470  
1471 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
1472 {
1473 // m_log.DebugFormat(
1474 // "[BASE HTTP SERVER]: HandleHTTPRequest for request to {0}, method {1}",
1475 // request.RawUrl, request.HttpMethod);
1476  
1477 switch (request.HttpMethod)
1478 {
1479 case "OPTIONS":
1480 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1481 return null;
1482  
1483 default:
1484 return HandleContentVerbs(request, response);
1485 }
1486 }
1487  
1488 private byte[] HandleContentVerbs(OSHttpRequest request, OSHttpResponse response)
1489 {
1490 // m_log.DebugFormat("[BASE HTTP SERVER]: HandleContentVerbs for request to {0}", request.RawUrl);
1491  
1492 // This is a test. There's a workable alternative.. as this way sucks.
1493 // We'd like to put this into a text file parhaps that's easily editable.
1494 //
1495 // For this test to work, I used the following secondlife.exe parameters
1496 // "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
1497 //
1498 // Even after all that, there's still an error, but it's a start.
1499 //
1500 // I depend on show_login_form being in the secondlife.exe parameters to figure out
1501 // to display the form, or process it.
1502 // a better way would be nifty.
1503  
1504 byte[] buffer;
1505  
1506 Stream requestStream = request.InputStream;
1507  
1508 Encoding encoding = Encoding.UTF8;
1509 StreamReader reader = new StreamReader(requestStream, encoding);
1510  
1511 string requestBody = reader.ReadToEnd();
1512 // avoid warning for now
1513 reader.ReadToEnd();
1514 reader.Close();
1515 requestStream.Close();
1516  
1517 Hashtable keysvals = new Hashtable();
1518 Hashtable headervals = new Hashtable();
1519  
1520 Hashtable requestVars = new Hashtable();
1521  
1522 string host = String.Empty;
1523  
1524 string[] querystringkeys = request.QueryString.AllKeys;
1525 string[] rHeaders = request.Headers.AllKeys;
1526  
1527 keysvals.Add("body", requestBody);
1528 keysvals.Add("uri", request.RawUrl);
1529 keysvals.Add("content-type", request.ContentType);
1530 keysvals.Add("http-method", request.HttpMethod);
1531  
1532 foreach (string queryname in querystringkeys)
1533 {
1534 // m_log.DebugFormat(
1535 // "[BASE HTTP SERVER]: Got query paremeter {0}={1}", queryname, request.QueryString[queryname]);
1536 keysvals.Add(queryname, request.QueryString[queryname]);
1537 requestVars.Add(queryname, keysvals[queryname]);
1538 }
1539  
1540 foreach (string headername in rHeaders)
1541 {
1542 // m_log.Debug("[BASE HTTP SERVER]: " + headername + "=" + request.Headers[headername]);
1543 headervals[headername] = request.Headers[headername];
1544 }
1545  
1546 if (headervals.Contains("Host"))
1547 {
1548 host = (string)headervals["Host"];
1549 }
1550  
1551 keysvals.Add("headers", headervals);
1552 keysvals.Add("querystringkeys", querystringkeys);
1553 keysvals.Add("requestvars", requestVars);
1554 // keysvals.Add("form", request.Form);
1555  
1556 if (keysvals.Contains("method"))
1557 {
1558 // m_log.Debug("[BASE HTTP SERVER]: Contains Method");
1559 string method = (string) keysvals["method"];
1560 // m_log.Debug("[BASE HTTP SERVER]: " + requestBody);
1561 GenericHTTPMethod requestprocessor;
1562 bool foundHandler = TryGetHTTPHandler(method, out requestprocessor);
1563 if (foundHandler)
1564 {
1565 Hashtable responsedata1 = requestprocessor(keysvals);
1566 buffer = DoHTTPGruntWork(responsedata1,response);
1567  
1568 //SendHTML500(response);
1569 }
1570 else
1571 {
1572 // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found");
1573 buffer = SendHTML404(response, host);
1574 }
1575 }
1576 else
1577 {
1578 GenericHTTPMethod requestprocessor;
1579 bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor);
1580 if (foundHandler)
1581 {
1582 Hashtable responsedata2 = requestprocessor(keysvals);
1583 buffer = DoHTTPGruntWork(responsedata2, response);
1584  
1585 //SendHTML500(response);
1586 }
1587 else
1588 {
1589 // m_log.Warn("[BASE HTTP SERVER]: Handler Not Found2");
1590 buffer = SendHTML404(response, host);
1591 }
1592 }
1593  
1594 return buffer;
1595 }
1596  
1597 private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler)
1598 {
1599 httpHandler = null;
1600 // Pull out the first part of the path
1601 // splitting the path by '/' means we'll get the following return..
1602 // {0}/{1}/{2}
1603 // where {0} isn't something we really control 100%
1604  
1605 string[] pathbase = path.Split('/');
1606 string searchquery = "/";
1607  
1608 if (pathbase.Length < 1)
1609 return false;
1610  
1611 for (int i = 1; i < pathbase.Length; i++)
1612 {
1613 searchquery += pathbase[i];
1614 if (pathbase.Length - 1 != i)
1615 searchquery += "/";
1616 }
1617  
1618 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1619 //
1620 // [] = optional
1621 // /resource/UUID/action[/action]
1622 //
1623 // now try to get the closest match to the reigstered path
1624 // at least for OGP, registered path would probably only consist of the /resource/
1625  
1626 string bestMatch = null;
1627  
1628 // m_log.DebugFormat(
1629 // "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery);
1630  
1631 lock (m_HTTPHandlers)
1632 {
1633 foreach (string pattern in m_HTTPHandlers.Keys)
1634 {
1635 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1636 {
1637 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1638 {
1639 // You have to specifically register for '/' and to get it, you must specifically request it
1640 if (pattern == "/" && searchquery == "/" || pattern != "/")
1641 bestMatch = pattern;
1642 }
1643 }
1644 }
1645  
1646 if (String.IsNullOrEmpty(bestMatch))
1647 {
1648 httpHandler = null;
1649 return false;
1650 }
1651 else
1652 {
1653 if (bestMatch == "/" && searchquery != "/")
1654 return false;
1655  
1656 httpHandler = m_HTTPHandlers[bestMatch];
1657 return true;
1658 }
1659 }
1660 }
1661  
1662 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
1663 {
1664 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
1665 int responsecode = (int)responsedata["int_response_code"];
1666 string responseString = (string)responsedata["str_response_string"];
1667 string contentType = (string)responsedata["content_type"];
1668  
1669 if (responsedata.ContainsKey("error_status_text"))
1670 {
1671 response.StatusDescription = (string)responsedata["error_status_text"];
1672 }
1673 if (responsedata.ContainsKey("http_protocol_version"))
1674 {
1675 response.ProtocolVersion = (string)responsedata["http_protocol_version"];
1676 }
1677  
1678 if (responsedata.ContainsKey("keepalive"))
1679 {
1680 bool keepalive = (bool)responsedata["keepalive"];
1681 response.KeepAlive = keepalive;
1682  
1683 }
1684  
1685 if (responsedata.ContainsKey("reusecontext"))
1686 response.ReuseContext = (bool) responsedata["reusecontext"];
1687  
1688 // Cross-Origin Resource Sharing with simple requests
1689 if (responsedata.ContainsKey("access_control_allow_origin"))
1690 response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]);
1691  
1692 //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
1693 //and should check for NullReferenceExceptions
1694  
1695 if (string.IsNullOrEmpty(contentType))
1696 {
1697 contentType = "text/html";
1698 }
1699  
1700 // The client ignores anything but 200 here for web login, so ensure that this is 200 for that
1701  
1702 response.StatusCode = responsecode;
1703  
1704 if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
1705 {
1706 response.RedirectLocation = (string)responsedata["str_redirect_location"];
1707 response.StatusCode = responsecode;
1708 }
1709  
1710 response.AddHeader("Content-Type", contentType);
1711  
1712 byte[] buffer;
1713  
1714 if (!(contentType.Contains("image")
1715 || contentType.Contains("x-shockwave-flash")
1716 || contentType.Contains("application/x-oar")
1717 || contentType.Contains("application/vnd.ll.mesh")))
1718 {
1719 // Text
1720 buffer = Encoding.UTF8.GetBytes(responseString);
1721 }
1722 else
1723 {
1724 // Binary!
1725 buffer = Convert.FromBase64String(responseString);
1726 }
1727  
1728 response.SendChunked = false;
1729 response.ContentLength64 = buffer.Length;
1730 response.ContentEncoding = Encoding.UTF8;
1731  
1732 return buffer;
1733 }
1734  
1735 public byte[] SendHTML404(OSHttpResponse response, string host)
1736 {
1737 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1738 response.StatusCode = 404;
1739 response.AddHeader("Content-type", "text/html");
1740  
1741 string responseString = GetHTTP404(host);
1742 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1743  
1744 response.SendChunked = false;
1745 response.ContentLength64 = buffer.Length;
1746 response.ContentEncoding = Encoding.UTF8;
1747  
1748 return buffer;
1749 }
1750  
1751 public byte[] SendHTML500(OSHttpResponse response)
1752 {
1753 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1754 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1755 response.AddHeader("Content-type", "text/html");
1756  
1757 string responseString = GetHTTP500();
1758 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1759  
1760 response.SendChunked = false;
1761 response.ContentLength64 = buffer.Length;
1762 response.ContentEncoding = Encoding.UTF8;
1763  
1764  
1765 return buffer;
1766 }
1767  
1768 public void Start()
1769 {
1770 StartHTTP();
1771 }
1772  
1773 private void StartHTTP()
1774 {
1775 m_log.InfoFormat(
1776 "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port);
1777  
1778 try
1779 {
1780 //m_httpListener = new HttpListener();
1781  
1782 NotSocketErrors = 0;
1783 if (!m_ssl)
1784 {
1785 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1786 //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/");
1787 m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port);
1788 m_httpListener2.ExceptionThrown += httpServerException;
1789 m_httpListener2.LogWriter = httpserverlog;
1790  
1791 // Uncomment this line in addition to those in HttpServerLogWriter
1792 // if you want more detailed trace information from the HttpServer
1793 //m_httpListener2.UseTraceLogs = true;
1794  
1795 //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor;
1796 }
1797 else
1798 {
1799 //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/");
1800 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1801 m_httpListener2 = CoolHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert);
1802 m_httpListener2.ExceptionThrown += httpServerException;
1803 m_httpListener2.LogWriter = httpserverlog;
1804 }
1805  
1806 m_httpListener2.RequestReceived += OnRequest;
1807 //m_httpListener.Start();
1808 m_httpListener2.Start(64);
1809  
1810 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
1811 m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000);
1812 m_PollServiceManager.Start();
1813 HTTPDRunning = true;
1814  
1815 //HttpListenerContext context;
1816 //while (true)
1817 //{
1818 // context = m_httpListener.GetContext();
1819 // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context);
1820 // }
1821 }
1822 catch (Exception e)
1823 {
1824 m_log.Error("[BASE HTTP SERVER]: Error - " + e.Message);
1825 m_log.Error("[BASE HTTP SERVER]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?");
1826  
1827 // We want this exception to halt the entire server since in current configurations we aren't too
1828 // useful without inbound HTTP.
1829 throw e;
1830 }
1831  
1832 m_requestsProcessedStat
1833 = new Stat(
1834 "HTTPRequestsServed",
1835 "Number of inbound HTTP requests processed",
1836 "",
1837 "requests",
1838 "httpserver",
1839 Port.ToString(),
1840 StatType.Pull,
1841 MeasuresOfInterest.AverageChangeOverTime,
1842 stat => stat.Value = RequestNumber,
1843 StatVerbosity.Debug);
1844  
1845 StatsManager.RegisterStat(m_requestsProcessedStat);
1846 }
1847  
1848 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
1849 {
1850 switch (err)
1851 {
1852 case SocketError.NotSocket:
1853 NotSocketErrors++;
1854  
1855 break;
1856 }
1857 }
1858  
1859 public void httpServerException(object source, Exception exception)
1860 {
1861 m_log.Error(String.Format("[BASE HTTP SERVER]: {0} had an exception: {1} ", source.ToString(), exception.Message), exception);
1862 /*
1863 if (HTTPDRunning)// && NotSocketErrors > 5)
1864 {
1865 Stop();
1866 Thread.Sleep(200);
1867 StartHTTP();
1868 m_log.Warn("[HTTPSERVER]: Died. Trying to kick.....");
1869 }
1870 */
1871 }
1872  
1873 public void Stop()
1874 {
1875 HTTPDRunning = false;
1876  
1877 StatsManager.DeregisterStat(m_requestsProcessedStat);
1878  
1879 try
1880 {
1881 m_PollServiceManager.Stop();
1882  
1883 m_httpListener2.ExceptionThrown -= httpServerException;
1884 //m_httpListener2.DisconnectHandler = null;
1885  
1886 m_httpListener2.LogWriter = null;
1887 m_httpListener2.RequestReceived -= OnRequest;
1888 m_httpListener2.Stop();
1889 }
1890 catch (NullReferenceException)
1891 {
1892 m_log.Warn("[BASE HTTP SERVER]: Null Reference when stopping HttpServer.");
1893 }
1894 }
1895  
1896 public void RemoveStreamHandler(string httpMethod, string path)
1897 {
1898 string handlerKey = GetHandlerKey(httpMethod, path);
1899  
1900 //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey);
1901  
1902 lock (m_streamHandlers)
1903 m_streamHandlers.Remove(handlerKey);
1904 }
1905  
1906 public void RemoveHTTPHandler(string httpMethod, string path)
1907 {
1908 lock (m_HTTPHandlers)
1909 {
1910 if (httpMethod != null && httpMethod.Length == 0)
1911 {
1912 m_HTTPHandlers.Remove(path);
1913 return;
1914 }
1915  
1916 m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path));
1917 }
1918 }
1919  
1920 public void RemovePollServiceHTTPHandler(string httpMethod, string path)
1921 {
1922 lock (m_pollHandlers)
1923 m_pollHandlers.Remove(path);
1924 }
1925  
1926 // public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
1927 // {
1928 // lock (m_agentHandlers)
1929 // {
1930 // IHttpAgentHandler foundHandler;
1931 //
1932 // if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
1933 // {
1934 // m_agentHandlers.Remove(agent);
1935 // return true;
1936 // }
1937 // }
1938 //
1939 // return false;
1940 // }
1941  
1942 public void RemoveXmlRPCHandler(string method)
1943 {
1944 lock (m_rpcHandlers)
1945 m_rpcHandlers.Remove(method);
1946 }
1947  
1948 public void RemoveJsonRPCHandler(string method)
1949 {
1950 lock(jsonRpcHandlers)
1951 jsonRpcHandlers.Remove(method);
1952 }
1953  
1954 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
1955 {
1956 lock (m_llsdHandlers)
1957 {
1958 LLSDMethod foundHandler;
1959  
1960 if (m_llsdHandlers.TryGetValue(path, out foundHandler) && foundHandler == handler)
1961 {
1962 m_llsdHandlers.Remove(path);
1963 return true;
1964 }
1965 }
1966  
1967 return false;
1968 }
1969  
1970 public string GetHTTP404(string host)
1971 {
1972 string file = Path.Combine(".", "http_404.html");
1973 if (!File.Exists(file))
1974 return getDefaultHTTP404(host);
1975  
1976 StreamReader sr = File.OpenText(file);
1977 string result = sr.ReadToEnd();
1978 sr.Close();
1979 return result;
1980 }
1981  
1982 public string GetHTTP500()
1983 {
1984 string file = Path.Combine(".", "http_500.html");
1985 if (!File.Exists(file))
1986 return getDefaultHTTP500();
1987  
1988 StreamReader sr = File.OpenText(file);
1989 string result = sr.ReadToEnd();
1990 sr.Close();
1991 return result;
1992 }
1993  
1994 // Fallback HTTP responses in case the HTTP error response files don't exist
1995 private static string getDefaultHTTP404(string host)
1996 {
1997 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>";
1998 }
1999  
2000 private static string getDefaultHTTP500()
2001 {
2002 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>";
2003 }
2004 }
2005  
2006 public class HttpServerContextObj
2007 {
2008 public IHttpClientContext context = null;
2009 public IHttpRequest req = null;
2010 public OSHttpRequest oreq = null;
2011 public OSHttpResponse oresp = null;
2012  
2013 public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs)
2014 {
2015 context = contxt;
2016 req = reqs;
2017 }
2018  
2019 public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp)
2020 {
2021 oreq = osreq;
2022 oresp = osresp;
2023 }
2024 }
2025  
2026 /// <summary>
2027 /// Relays HttpServer log messages to our own logging mechanism.
2028 /// </summary>
2029 /// To use this you must uncomment the switch section
2030 ///
2031 /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs
2032 /// property in StartHttp() for the HttpListener
2033 public class HttpServerLogWriter : ILogWriter
2034 {
2035 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
2036  
2037 public void Write(object source, LogPrio priority, string message)
2038 {
2039 /*
2040 switch (priority)
2041 {
2042 case LogPrio.Trace:
2043 m_log.DebugFormat("[{0}]: {1}", source, message);
2044 break;
2045 case LogPrio.Debug:
2046 m_log.DebugFormat("[{0}]: {1}", source, message);
2047 break;
2048 case LogPrio.Error:
2049 m_log.ErrorFormat("[{0}]: {1}", source, message);
2050 break;
2051 case LogPrio.Info:
2052 m_log.InfoFormat("[{0}]: {1}", source, message);
2053 break;
2054 case LogPrio.Warning:
2055 m_log.WarnFormat("[{0}]: {1}", source, message);
2056 break;
2057 case LogPrio.Fatal:
2058 m_log.ErrorFormat("[{0}]: FATAL! - {1}", source, message);
2059 break;
2060 default:
2061 break;
2062 }
2063 */
2064  
2065 return;
2066 }
2067 }
2068 }