diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/Servers/BaseHttpServer.cs | 338 |
1 files changed, 215 insertions, 123 deletions
diff --git a/OpenSim/Framework/Servers/BaseHttpServer.cs b/OpenSim/Framework/Servers/BaseHttpServer.cs index 004b330..32d65af 100644 --- a/OpenSim/Framework/Servers/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/BaseHttpServer.cs | |||
@@ -46,6 +46,8 @@ namespace OpenSim.Framework.Servers | |||
46 | protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); | 46 | protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); |
47 | protected LLSDMethod m_llsdHandler = null; | 47 | protected LLSDMethod m_llsdHandler = null; |
48 | protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>(); | 48 | protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>(); |
49 | protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>(); | ||
50 | |||
49 | protected uint m_port; | 51 | protected uint m_port; |
50 | protected bool m_ssl = false; | 52 | protected bool m_ssl = false; |
51 | protected bool m_firstcaps = true; | 53 | protected bool m_firstcaps = true; |
@@ -92,6 +94,18 @@ namespace OpenSim.Framework.Servers | |||
92 | return false; | 94 | return false; |
93 | } | 95 | } |
94 | 96 | ||
97 | public bool AddHTTPHandler(string method, GenericHTTPMethod handler) | ||
98 | { | ||
99 | if (!m_HTTPHandlers.ContainsKey(method)) | ||
100 | { | ||
101 | m_HTTPHandlers.Add(method, handler); | ||
102 | return true; | ||
103 | } | ||
104 | |||
105 | //must already have a handler for that path so return false | ||
106 | return false; | ||
107 | } | ||
108 | |||
95 | public bool SetLLSDHandler(LLSDMethod handler) | 109 | public bool SetLLSDHandler(LLSDMethod handler) |
96 | { | 110 | { |
97 | m_llsdHandler = handler; | 111 | m_llsdHandler = handler; |
@@ -145,6 +159,10 @@ namespace OpenSim.Framework.Servers | |||
145 | { | 159 | { |
146 | switch (request.ContentType) | 160 | switch (request.ContentType) |
147 | { | 161 | { |
162 | case null: | ||
163 | case "text/html": | ||
164 | HandleHTTPRequest(request, response); | ||
165 | break; | ||
148 | case "application/xml+llsd": | 166 | case "application/xml+llsd": |
149 | HandleLLSDRequests(request, response); | 167 | HandleLLSDRequests(request, response); |
150 | break; | 168 | break; |
@@ -184,6 +202,32 @@ namespace OpenSim.Framework.Servers | |||
184 | } | 202 | } |
185 | } | 203 | } |
186 | 204 | ||
205 | private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler) | ||
206 | { | ||
207 | string bestMatch = null; | ||
208 | |||
209 | foreach (string pattern in m_HTTPHandlers.Keys) | ||
210 | { | ||
211 | if (handlerKey.StartsWith(pattern)) | ||
212 | { | ||
213 | if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) | ||
214 | { | ||
215 | bestMatch = pattern; | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | |||
220 | if (String.IsNullOrEmpty(bestMatch)) | ||
221 | { | ||
222 | HTTPHandler = null; | ||
223 | return false; | ||
224 | } | ||
225 | else | ||
226 | { | ||
227 | HTTPHandler = m_HTTPHandlers[bestMatch]; | ||
228 | return true; | ||
229 | } | ||
230 | } | ||
187 | private void HandleXmlRpcRequests(HttpListenerRequest request, HttpListenerResponse response) | 231 | private void HandleXmlRpcRequests(HttpListenerRequest request, HttpListenerResponse response) |
188 | { | 232 | { |
189 | Stream requestStream = request.InputStream; | 233 | Stream requestStream = request.InputStream; |
@@ -204,27 +248,7 @@ namespace OpenSim.Framework.Servers | |||
204 | } | 248 | } |
205 | catch (XmlException e) | 249 | catch (XmlException e) |
206 | { | 250 | { |
207 | Hashtable keysvals = new Hashtable(); | 251 | |
208 | responseString = String.Format("XmlException:\n{0}", e.Message); | ||
209 | MainLog.Instance.Error("XML", responseString); | ||
210 | string[] querystringkeys = request.QueryString.AllKeys; | ||
211 | string[] rHeaders = request.Headers.AllKeys; | ||
212 | |||
213 | |||
214 | foreach (string queryname in querystringkeys) | ||
215 | { | ||
216 | keysvals.Add(queryname, request.QueryString[queryname]); | ||
217 | MainLog.Instance.Warn("HTTP", queryname + "=" + request.QueryString[queryname]); | ||
218 | } | ||
219 | foreach (string headername in rHeaders) | ||
220 | { | ||
221 | MainLog.Instance.Warn("HEADER", headername + "=" + request.Headers[headername]); | ||
222 | } | ||
223 | if (keysvals.ContainsKey("show_login_form")) | ||
224 | { | ||
225 | HandleHTTPRequest(keysvals, request, response); | ||
226 | return; | ||
227 | } | ||
228 | } | 252 | } |
229 | 253 | ||
230 | if (xmlRprcRequest != null) | 254 | if (xmlRprcRequest != null) |
@@ -332,7 +356,7 @@ namespace OpenSim.Framework.Servers | |||
332 | } | 356 | } |
333 | } | 357 | } |
334 | 358 | ||
335 | public void HandleHTTPRequest(Hashtable keysvals, HttpListenerRequest request, HttpListenerResponse response) | 359 | public void HandleHTTPRequest(HttpListenerRequest request, HttpListenerResponse response) |
336 | { | 360 | { |
337 | // This is a test. There's a workable alternative.. as this way sucks. | 361 | // This is a test. There's a workable alternative.. as this way sucks. |
338 | // We'd like to put this into a text file parhaps that's easily editable. | 362 | // We'd like to put this into a text file parhaps that's easily editable. |
@@ -345,119 +369,146 @@ namespace OpenSim.Framework.Servers | |||
345 | // I depend on show_login_form being in the secondlife.exe parameters to figure out | 369 | // I depend on show_login_form being in the secondlife.exe parameters to figure out |
346 | // to display the form, or process it. | 370 | // to display the form, or process it. |
347 | // a better way would be nifty. | 371 | // a better way would be nifty. |
372 | Stream requestStream = request.InputStream; | ||
348 | 373 | ||
349 | if ((string) keysvals["show_login_form"] == "TRUE") | 374 | Encoding encoding = Encoding.UTF8; |
350 | { | 375 | StreamReader reader = new StreamReader(requestStream, encoding); |
351 | string responseString = | 376 | |
352 | "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"; | 377 | string requestBody = reader.ReadToEnd(); |
353 | responseString = responseString + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"; | 378 | reader.Close(); |
354 | responseString = responseString + "<head>"; | 379 | requestStream.Close(); |
355 | responseString = responseString + | 380 | |
356 | "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"; | 381 | string responseString = String.Empty; |
357 | responseString = responseString + "<meta http-equiv=\"cache-control\" content=\"no-cache\">"; | 382 | |
358 | responseString = responseString + "<meta http-equiv=\"Pragma\" content=\"no-cache\">"; | 383 | Hashtable keysvals = new Hashtable(); |
359 | responseString = responseString + "<title>Second Life Login</title>"; | 384 | |
360 | responseString = responseString + "<body>"; | 385 | string[] querystringkeys = request.QueryString.AllKeys; |
361 | responseString = responseString + "<div id=\"login_box\">"; | 386 | string[] rHeaders = request.Headers.AllKeys; |
362 | // Linden Grid Form Post | 387 | |
363 | //responseString = responseString + "<form action=\"https://secure-web16.secondlife.com/app/login/go.php?show_login_form=True&show_grid=&show_start_location=\" method=\"POST\" id=\"login-form\">"; | 388 | |
364 | responseString = responseString + "<form action=\"/\" method=\"GET\" id=\"login-form\">"; | 389 | foreach (string queryname in querystringkeys) |
365 | 390 | { | |
366 | responseString = responseString + "<div id=\"message\">"; | 391 | keysvals.Add(queryname, request.QueryString[queryname]); |
367 | responseString = responseString + "</div>"; | 392 | MainLog.Instance.Warn("HTTP", queryname + "=" + request.QueryString[queryname]); |
368 | responseString = responseString + "<fieldset id=\"firstname\">"; | 393 | } |
369 | responseString = responseString + "<legend>First Name:</legend>"; | 394 | //foreach (string headername in rHeaders) |
370 | responseString = responseString + | 395 | //{ |
371 | "<input type=\"text\" id=\"firstname_input\" size=\"15\" maxlength=\"100\" name=\"username\" value=\"" + | 396 | //MainLog.Instance.Warn("HEADER", headername + "=" + request.Headers[headername]); |
372 | keysvals["username"] + "\" />"; | 397 | //} |
373 | responseString = responseString + "</fieldset>"; | 398 | if (keysvals.Contains("method")) |
374 | responseString = responseString + "<fieldset id=\"lastname\">"; | 399 | { |
375 | responseString = responseString + "<legend>Last Name:</legend>"; | 400 | MainLog.Instance.Warn("HTTP", "Contains Method"); |
376 | responseString = responseString + | 401 | string method = (string) keysvals["method"]; |
377 | "<input type=\"text\" size=\"15\" maxlength=\"100\" name=\"lastname\" value=\"" + | 402 | MainLog.Instance.Warn("HTTP", requestBody); |
378 | keysvals["lastname"] + "\" />"; | 403 | GenericHTTPMethod requestprocessor; |
379 | responseString = responseString + "</fieldset>"; | 404 | bool foundHandler = TryGetHTTPHandler(method, out requestprocessor); |
380 | responseString = responseString + "<fieldset id=\"password\">"; | 405 | if (foundHandler) |
381 | responseString = responseString + "<legend>Password:</legend>"; | ||
382 | responseString = responseString + "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">"; | ||
383 | responseString = responseString + "<tr>"; | ||
384 | responseString = responseString + | ||
385 | "<td colspan=\"2\"><input type=\"password\" size=\"15\" maxlength=\"100\" name=\"password\" value=\"" + | ||
386 | keysvals["password"] + "\" /></td>"; | ||
387 | responseString = responseString + "</tr>"; | ||
388 | responseString = responseString + "<tr>"; | ||
389 | responseString = responseString + | ||
390 | "<td valign=\"middle\"><input type=\"checkbox\" name=\"remember_password\" id=\"remember_password\" value=\"" + | ||
391 | keysvals["remember_password"] + "\" checked style=\"margin-left:0px;\"/></td>"; | ||
392 | responseString = responseString + "<td><label for=\"remember_password\">Remember password</label></td>"; | ||
393 | responseString = responseString + "</tr>"; | ||
394 | responseString = responseString + "</table>"; | ||
395 | responseString = responseString + "</fieldset>"; | ||
396 | responseString = responseString + "<input type=\"hidden\" name=\"show_login_form\" value=\"FALSE\" />"; | ||
397 | responseString = responseString + "<input type=\"hidden\" id=\"grid\" name=\"grid\" value=\"" + | ||
398 | keysvals["grid"] + "\" />"; | ||
399 | responseString = responseString + "<div id=\"submitbtn\">"; | ||
400 | responseString = responseString + "<input class=\"input_over\" type=\"submit\" value=\"Connect\" />"; | ||
401 | responseString = responseString + "</div>"; | ||
402 | responseString = responseString + | ||
403 | "<div id=\"connecting\" style=\"visibility:hidden\"><img src=\"/_img/sl_logo_rotate_black.gif\" align=\"absmiddle\"> Connecting...</div>"; | ||
404 | |||
405 | responseString = responseString + "<div id=\"helplinks\">"; | ||
406 | responseString = responseString + | ||
407 | "<a href=\"http://www.secondlife.com/join/index.php\" target=\"_blank\">Create new account</a> | "; | ||
408 | responseString = responseString + | ||
409 | "<a href=\"http://www.secondlife.com/account/request.php\" target=\"_blank\">Forgot password?</a>"; | ||
410 | responseString = responseString + "</div>"; | ||
411 | |||
412 | responseString = responseString + "<div id=\"channelinfo\"> " + keysvals["channel"] + " | " + | ||
413 | keysvals["version"] + "=" + keysvals["lang"] + "</div>"; | ||
414 | responseString = responseString + "</form>"; | ||
415 | responseString = responseString + "<script language=\"JavaScript\">"; | ||
416 | responseString = responseString + "document.getElementById('firstname_input').focus();"; | ||
417 | responseString = responseString + "</script>"; | ||
418 | responseString = responseString + "</div>"; | ||
419 | responseString = responseString + "</div>"; | ||
420 | responseString = responseString + "</body>"; | ||
421 | responseString = responseString + "</html>"; | ||
422 | response.AddHeader("Content-type", "text/html"); | ||
423 | |||
424 | byte[] buffer = Encoding.UTF8.GetBytes(responseString); | ||
425 | |||
426 | response.SendChunked = false; | ||
427 | response.ContentLength64 = buffer.Length; | ||
428 | response.ContentEncoding = Encoding.UTF8; | ||
429 | try | ||
430 | { | ||
431 | response.OutputStream.Write(buffer, 0, buffer.Length); | ||
432 | } | ||
433 | catch (Exception ex) | ||
434 | { | 406 | { |
435 | MainLog.Instance.Warn("HTTPD", "Error - " + ex.Message); | 407 | Hashtable responsedata = requestprocessor(keysvals); |
408 | DoHTTPGruntWork(responsedata,response); | ||
409 | |||
410 | //SendHTML500(response); | ||
411 | |||
436 | } | 412 | } |
437 | finally | 413 | else |
438 | { | 414 | { |
439 | response.OutputStream.Close(); | 415 | MainLog.Instance.Warn("HTTP", "Handler Not Found"); |
416 | SendHTML404(response); | ||
440 | } | 417 | } |
441 | } // show_login_form == "TRUE" | 418 | } |
442 | else | 419 | else |
443 | { | 420 | { |
444 | // show_login_form is present but FALSE | 421 | MainLog.Instance.Warn("HTTP", "No Method specified"); |
445 | // | 422 | SendHTML404(response); |
446 | // The idea here is that we're telling the client to log in immediately here using the following information | 423 | } |
447 | // For my testing, I'm hard coding the web_login_key temporarily. | 424 | } |
448 | // Telling the client to go to the new improved SLURL for immediate logins | 425 | |
426 | private void DoHTTPGruntWork(Hashtable responsedata, HttpListenerResponse response) | ||
427 | { | ||
428 | int responsecode = (int)responsedata["int_response_code"]; | ||
429 | string responseString = (string)responsedata["str_response_string"]; | ||
430 | |||
431 | // We're forgoing the usual error status codes here because the client | ||
432 | // ignores anything but 200 and 301 | ||
449 | 433 | ||
450 | // The fact that it says grid=Other is important | 434 | response.StatusCode = 200; |
451 | 435 | ||
452 | // | 436 | if (responsecode == 301) |
437 | { | ||
438 | response.RedirectLocation = (string)responsedata["str_redirect_location"]; | ||
439 | response.StatusCode = responsecode; | ||
440 | } | ||
441 | response.AddHeader("Content-type", "text/html"); | ||
453 | 442 | ||
454 | response.StatusCode = 301; | 443 | byte[] buffer = Encoding.UTF8.GetBytes(responseString); |
455 | response.RedirectLocation = "secondlife:///app/login?first_name=" + keysvals["username"] + "&last_name=" + | ||
456 | keysvals["lastname"] + | ||
457 | "&location=home&grid=other&web_login_key=796f2b2a-0131-41e4-af12-00f60c24c458"; | ||
458 | 444 | ||
445 | response.SendChunked = false; | ||
446 | response.ContentLength64 = buffer.Length; | ||
447 | response.ContentEncoding = Encoding.UTF8; | ||
448 | try | ||
449 | { | ||
450 | response.OutputStream.Write(buffer, 0, buffer.Length); | ||
451 | } | ||
452 | catch (Exception ex) | ||
453 | { | ||
454 | MainLog.Instance.Warn("HTTPD", "Error - " + ex.Message); | ||
455 | } | ||
456 | finally | ||
457 | { | ||
459 | response.OutputStream.Close(); | 458 | response.OutputStream.Close(); |
460 | } // show_login_form == "FALSE" | 459 | } |
460 | |||
461 | |||
462 | } | ||
463 | public void SendHTML404(HttpListenerResponse response) | ||
464 | { | ||
465 | // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s | ||
466 | response.StatusCode = 200; | ||
467 | response.AddHeader("Content-type", "text/html"); | ||
468 | |||
469 | string responseString = GetHTTP404(); | ||
470 | byte[] buffer = Encoding.UTF8.GetBytes(responseString); | ||
471 | |||
472 | response.SendChunked = false; | ||
473 | response.ContentLength64 = buffer.Length; | ||
474 | response.ContentEncoding = Encoding.UTF8; | ||
475 | try | ||
476 | { | ||
477 | response.OutputStream.Write(buffer, 0, buffer.Length); | ||
478 | } | ||
479 | catch (Exception ex) | ||
480 | { | ||
481 | MainLog.Instance.Warn("HTTPD", "Error - " + ex.Message); | ||
482 | } | ||
483 | finally | ||
484 | { | ||
485 | response.OutputStream.Close(); | ||
486 | } | ||
487 | } | ||
488 | public void SendHTML500(HttpListenerResponse response) | ||
489 | { | ||
490 | // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s | ||
491 | response.StatusCode = 200; | ||
492 | response.AddHeader("Content-type", "text/html"); | ||
493 | |||
494 | string responseString = GetHTTP500(); | ||
495 | byte[] buffer = Encoding.UTF8.GetBytes(responseString); | ||
496 | |||
497 | response.SendChunked = false; | ||
498 | response.ContentLength64 = buffer.Length; | ||
499 | response.ContentEncoding = Encoding.UTF8; | ||
500 | try | ||
501 | { | ||
502 | response.OutputStream.Write(buffer, 0, buffer.Length); | ||
503 | } | ||
504 | catch (Exception ex) | ||
505 | { | ||
506 | MainLog.Instance.Warn("HTTPD", "Error - " + ex.Message); | ||
507 | } | ||
508 | finally | ||
509 | { | ||
510 | response.OutputStream.Close(); | ||
511 | } | ||
461 | } | 512 | } |
462 | 513 | ||
463 | public void Start() | 514 | public void Start() |
@@ -504,5 +555,46 @@ namespace OpenSim.Framework.Servers | |||
504 | { | 555 | { |
505 | m_streamHandlers.Remove(GetHandlerKey(httpMethod, path)); | 556 | m_streamHandlers.Remove(GetHandlerKey(httpMethod, path)); |
506 | } | 557 | } |
558 | |||
559 | public void RemoveHTTPHandler(string httpMethod, string path) | ||
560 | { | ||
561 | m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path)); | ||
562 | } | ||
563 | |||
564 | public string GetHTTP404() | ||
565 | { | ||
566 | string file = Path.Combine(Util.configDir(), "http_404.html"); | ||
567 | if (!File.Exists(file)) | ||
568 | return getDefaultHTTP404(); | ||
569 | |||
570 | StreamReader sr = File.OpenText(file); | ||
571 | string result = sr.ReadToEnd(); | ||
572 | sr.Close(); | ||
573 | return result; | ||
574 | } | ||
575 | |||
576 | public string GetHTTP500() | ||
577 | { | ||
578 | string file = Path.Combine(Util.configDir(), "http_500.html"); | ||
579 | if (!File.Exists(file)) | ||
580 | return getDefaultHTTP500(); | ||
581 | |||
582 | StreamReader sr = File.OpenText(file); | ||
583 | string result = sr.ReadToEnd(); | ||
584 | sr.Close(); | ||
585 | return result; | ||
586 | } | ||
587 | |||
588 | // Fallback HTTP responses in case the HTTP error response files don't exist | ||
589 | private string getDefaultHTTP404() | ||
590 | { | ||
591 | 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></BODY></HTML>"; | ||
592 | } | ||
593 | |||
594 | private string getDefaultHTTP500() | ||
595 | { | ||
596 | 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>"; | ||
597 | } | ||
598 | |||
507 | } | 599 | } |
508 | } | 600 | } |