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