/// <summary> /// Try all the registered xmlrpc handlers when an xmlrpc request is received. /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method. /// </summary> /// <param name="request"></param> /// <param name="response"></param> private bool HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) { Stream requestStream = request.InputStream; Encoding encoding = Encoding.UTF8; StreamReader reader = new StreamReader(requestStream, encoding); string requestBody = reader.ReadToEnd(); reader.Close(); requestStream.Close(); //MainConsole.Instance.Debug(requestBody); requestBody = requestBody.Replace("<base64></base64>", ""); XmlRpcRequest xmlRprcRequest = null; try { if(requestBody.StartsWith("<?xml")) xmlRprcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody); } catch (XmlException) { } if (xmlRprcRequest != null) { string methodName = xmlRprcRequest.MethodName; if (methodName != null) { xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1] XmlRpcResponse xmlRpcResponse; XmlRpcMethod method; bool methodWasFound; lock (m_rpcHandlers) { methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method); } if (methodWasFound) { xmlRprcRequest.Params.Add(request.Url); // Param[2] xmlRprcRequest.Params.Add(request.Headers.Get("X-Forwarded-For")); // Param[3] try { xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint); } catch (Exception e) { string errorMessage = String.Format( "Requested method [{0}] from {1} threw exception: {2} {3}", methodName, request.RemoteIPEndPoint.Address, e, e.StackTrace); MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: {0}", errorMessage); // if the registered XmlRpc method threw an exception, we pass a fault-code along xmlRpcResponse = new XmlRpcResponse(); // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php xmlRpcResponse.SetFault(-32603, errorMessage); } // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here response.KeepAlive = m_rpcHandlersKeepAlive[methodName]; } else { xmlRpcResponse = new XmlRpcResponse(); // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php xmlRpcResponse.SetFault( XmlRpcErrorCodes.SERVER_ERROR_METHOD, String.Format("Requested method [{0}] not found", methodName)); } string responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); response.ContentType = "text/xml"; byte[] buffer = Encoding.UTF8.GetBytes(responseString); response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; try { response.OutputStream.Write(buffer, 0, buffer.Length); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } finally { try { response.Send(); //response.FreeContext(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } } return true; } //HandleLLSDRequests(request, response); response.ContentType = "text/plain"; response.StatusCode = 404; response.StatusDescription = "Not Found"; response.ProtocolVersion = "HTTP/1.0"; byte[] buf = Encoding.UTF8.GetBytes("Not found"); response.KeepAlive = false; MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: Handler not found for http request {0}, port {1}", request.RawUrl, Port); response.SendChunked = false; response.ContentLength64 = buf.Length; response.ContentEncoding = Encoding.UTF8; try { response.OutputStream.Write(buf, 0, buf.Length); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } finally { try { response.Send(); //response.FreeContext(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } } return true; //responseString = "Error"; } return false; }
private void HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) { //MainConsole.Instance.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); Stream requestStream = request.InputStream; Encoding encoding = Encoding.UTF8; StreamReader reader = new StreamReader(requestStream, encoding); string requestBody = reader.ReadToEnd(); reader.Close(); requestStream.Close(); //MainConsole.Instance.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody); response.KeepAlive = true; OSD llsdRequest = null; OSD llsdResponse = null; bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest")); if (requestBody.Length == 0) // Get Request { requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>"; } try { llsdRequest = OSDParser.Deserialize(requestBody); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } if (llsdRequest != null)// && m_defaultLlsdHandler != null) { LLSDMethod llsdhandler = null; if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV) { // we found a registered llsd handler to service this request llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint); } else { // we didn't find a registered llsd handler to service this request // .. give em the failed message llsdResponse = GenerateNoLLSDHandlerResponse(); } } else { llsdResponse = GenerateNoLLSDHandlerResponse(); } byte[] buffer = new byte[0]; if (llsdResponse.ToString() == "shutdown404!") { response.ContentType = "text/plain"; response.StatusCode = 404; response.StatusDescription = "Not Found"; response.ProtocolVersion = "HTTP/1.0"; buffer = Encoding.UTF8.GetBytes("Not found"); } else { // Select an appropriate response format buffer = BuildLLSDResponse(request, response, llsdResponse); } response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; response.KeepAlive = true; try { response.OutputStream.Write(buffer, 0, buffer.Length); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } finally { //response.OutputStream.Close(); try { response.Send(); response.OutputStream.Flush(); //response.FreeContext(); //response.OutputStream.Close(); } catch (IOException e) { MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: LLSD IOException {0}.", e); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: LLSD issue {0}.\nNOTE: this may be spurious on Linux.", e); } } }
public void SendHTML500(OSHttpResponse response) { // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s response.StatusCode = (int)OSHttpStatusCode.SuccessOk; response.AddHeader("Content-type", "text/html"); string responseString = GetHTTP500(); byte[] buffer = Encoding.UTF8.GetBytes(responseString); response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; try { response.OutputStream.Write(buffer, 0, buffer.Length); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } finally { //response.OutputStream.Close(); try { response.Send(); //response.FreeContext(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } } }
// public void ConvertIHttpClientContextToOSHttp(object stateinfo) // { // HttpServerContextObj objstate = (HttpServerContextObj)stateinfo; // OSHttpRequest request = objstate.oreq; // OSHttpResponse resp = objstate.oresp; // HandleRequest(request,resp); // } /// <summary> /// This methods is the start of incoming HTTP request handling. /// </summary> /// <param name="request"></param> /// <param name="response"></param> public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response) { if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread { try { SendHTML500(response); } catch { } return; } string reqnum = "unknown"; int tickstart = Environment.TickCount; string RawUrl = request.RawUrl; string HTTPMethod = request.HttpMethod; try { if (request.Headers["opensim-request-id"] != null) reqnum = String.Format("{0}:{1}", request.RemoteIPEndPoint, request.Headers["opensim-request-id"]); // MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); //Fix the current Culture Culture.SetCurrentCulture(); //response.KeepAlive = true; response.SendChunked = false; IRequestHandler requestHandler; string path = request.RawUrl; string handlerKey = GetHandlerKey(request.HttpMethod, path); // MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: Handling {0} request for {1}", request.HttpMethod, path); if (TryGetStreamHandler(handlerKey, out requestHandler)) { //MainConsole.Instance.Debug("[BASE HTTP SERVER]: Found Stream Handler"); // Okay, so this is bad, but should be considered temporary until everything is IStreamHandler. byte[] buffer; response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. if (requestHandler is IStreamedRequestHandler) { IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response); } else if (requestHandler is IGenericHTTPHandler) { //MainConsole.Instance.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler"); IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler; Stream requestStream = request.InputStream; Encoding encoding = Encoding.UTF8; StreamReader reader = new StreamReader(requestStream, encoding); string requestBody = reader.ReadToEnd(); reader.Close(); //requestStream.Close(); Hashtable keysvals = new Hashtable(); Hashtable headervals = new Hashtable(); //string host = String.Empty; string[] querystringkeys = request.QueryString.AllKeys; string[] rHeaders = request.Headers.AllKeys; foreach (string queryname in querystringkeys) { keysvals.Add(queryname, request.QueryString[queryname]); } foreach (string headername in rHeaders) { //MainConsole.Instance.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]); headervals[headername] = request.Headers[headername]; } // if (headervals.Contains("Host")) // { // host = (string)headervals["Host"]; // } keysvals.Add("requestbody", requestBody); keysvals.Add("headers", headervals); if (keysvals.Contains("method")) { //MainConsole.Instance.Warn("[HTTP]: Contains Method"); //string method = (string)keysvals["method"]; //MainConsole.Instance.Warn("[HTTP]: " + requestBody); } DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response, request); return; } else { IStreamHandler streamHandler = (IStreamHandler)requestHandler; using (MemoryStream memoryStream = new MemoryStream()) { streamHandler.Handle(path, request.InputStream, memoryStream, request, response); memoryStream.Flush(); buffer = memoryStream.ToArray(); } } request.InputStream.Close(); // HTTP IN support. The script engine takes it from here // Nothing to worry about for us. // if (buffer == null) return; if (!response.SendChunked) response.ContentLength64 = buffer.LongLength; try { response.OutputStream.Write(buffer, 0, buffer.Length); } catch (HttpListenerException) { MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: HTTP request abnormally terminated."); } // try { response.Send(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } response = null; //This makes timeouts VERY bad if enabled /*try { //Clean up after ourselves response.OutputStream.Close(); response.FreeContext(); } catch(Exception ex) { MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: ISSUE WITH CLEANUP {0}", ex.ToString()); }*/ return; } if (request.AcceptTypes != null && request.AcceptTypes.Length > 0) { #if (!ISWIN) foreach (string strAccept in request.AcceptTypes) { if (strAccept.Contains("application/llsd+xml") || strAccept.Contains("application/llsd+json")) { HandleLLSDRequests(request, response); return; } } #else if (request.AcceptTypes.Any(strAccept => strAccept.Contains("application/llsd+xml") || strAccept.Contains("application/llsd+json"))) { HandleLLSDRequests(request, response); return; } #endif } switch (request.ContentType) { case null: case "text/html": case "application/x-www-form-urlencoded": // MainConsole.Instance.DebugFormat( // "[BASE HTTP SERVER]: Found a text/html content type for request {0}", request.RawUrl); HandleHTTPRequest(request, response); return; case "application/llsd+xml": case "application/xml+llsd": case "application/llsd+json": //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: found a application/llsd+xml content type"); HandleLLSDRequests(request, response); return; case "text/xml": case "application/xml": case "application/json": default: //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: in default handler"); // Point of note.. the DoWeHaveA methods check for an EXACT path // if (request.RawUrl.Contains("/CAPS/EQG")) // { // int i = 1; // } //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler"); if (DoWeHaveALLSDHandler(request.RawUrl)) { //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: Found LLSD Handler"); HandleLLSDRequests(request, response); return; } // MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl); if (DoWeHaveAHTTPHandler(request.RawUrl)) { // MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: Found HTTP Handler for request {0}", request.RawUrl); HandleHTTPRequest(request, response); return; } //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: Generic XMLRPC"); // generic login request. if (!HandleXmlRpcRequests(request, response)) { SendHTML404(response, ""); } return; } } catch (SocketException e) { // At least on linux, it appears that if the client makes a request without requiring the response, // an unconnected socket exception is thrown when we close the response output stream. There's no // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore // the exception instead. // // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go // with the minimum first MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux", e); } catch (IOException) { MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw "); } catch (Exception e) { MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); SendHTML500(response); } finally { int tickdiff = Environment.TickCount - tickstart; if (MainConsole.Instance.IsEnabled(log4net.Core.Level.Trace)) MainConsole.Instance.TraceFormat("[BASE HTTP SERVER]: request for {0} on port {3}, {2} took {1} ms", RawUrl, tickdiff, HTTPMethod, Port); // Every month or so this will wrap and give bad numbers, not really a problem // since its just for reporting, 500ms limit can be adjusted if (tickdiff > 500) MainConsole.Instance.InfoFormat("[BASE HTTP SERVER]: slow request <{0}> for {1},{3} took {2} ms", reqnum, RawUrl, tickdiff, HTTPMethod); } }
/// <summary> /// Get the user's display name, currently not used? /// </summary> /// <param name = "mDhttpMethod"></param> /// <param name = "agentID"></param> /// <returns></returns> private byte[] ProcessGetDisplayName(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { //I've never seen this come in, so for now... do nothing NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); string[] ids = query.GetValues("ids"); string username = query.GetOne("username"); OSDMap map = new OSDMap(); OSDArray agents = new OSDArray(); OSDArray bad_ids = new OSDArray(); OSDArray bad_usernames = new OSDArray(); if (ids != null) { foreach (string id in ids) { UserAccount account = m_userService.GetUserAccount(UUID.Zero, UUID.Parse(id)); if (account != null) { IUserProfileInfo info = DataManager.RequestPlugin<IProfileConnector>().GetUserProfile(account.PrincipalID); if (info != null) PackUserInfo(info, account, ref agents); else PackUserInfo(info, account, ref agents); //else //Technically is right, but needs to be packed no matter what for OS based grids // bad_ids.Add (id); } } } else if (username != null) { UserAccount account = m_userService.GetUserAccount(UUID.Zero, username.Replace('.', ' ')); if (account != null) { IUserProfileInfo info = DataManager.RequestPlugin<IProfileConnector>().GetUserProfile(account.PrincipalID); if (info != null) PackUserInfo(info, account, ref agents); else bad_usernames.Add(username); } } map["agents"] = agents; map["bad_ids"] = bad_ids; map["bad_usernames"] = bad_usernames; byte[] m = OSDParser.SerializeLLSDXmlBytes(map); httpResponse.Body.Write(m, 0, m.Length); httpResponse.StatusCode = (int) HttpStatusCode.OK; httpResponse.Send(); return null; }
internal void DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response, OSHttpRequest request) { //MainConsole.Instance.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); byte[] buffer; if (responsedata.Count == 0) { response.StatusCode = 404; buffer = Encoding.UTF8.GetBytes("404"); response.OutputStream.Write(buffer, 0, buffer.Length); response.OutputStream.Close(); return; } int responsecode = (int)responsedata["int_response_code"]; string responseString = (string)responsedata["str_response_string"]; string contentType = (string)responsedata["content_type"]; if (responsedata.ContainsKey("error_status_text")) { response.StatusDescription = (string)responsedata["error_status_text"]; } if (responsedata.ContainsKey("http_protocol_version")) { response.ProtocolVersion = (string)responsedata["http_protocol_version"]; } if (responsedata.ContainsKey("keepalive")) { bool keepalive = (bool)responsedata["keepalive"]; response.KeepAlive = keepalive; } if (responsedata.ContainsKey("reusecontext")) response.ReuseContext = (bool)responsedata["reusecontext"]; //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this //and should check for NullReferenceExceptions if (string.IsNullOrEmpty(contentType)) { contentType = "text/html"; } // The client ignores anything but 200 here for web login, so ensure that this is 200 for that response.StatusCode = responsecode; if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently) { response.RedirectLocation = (string)responsedata["str_redirect_location"]; response.StatusCode = responsecode; } response.AddHeader("Content-Type", contentType); if (!(contentType.Contains("image") || contentType.Contains("x-shockwave-flash") || contentType.Contains("application/x-oar") || contentType.Contains("application/vnd.ll.mesh"))) { // Text buffer = Encoding.UTF8.GetBytes(responseString); } else { // Binary! buffer = Convert.FromBase64String(responseString); } bool sendBuffer = true; string ETag = Util.SHA1Hash(buffer.ToString()); response.AddHeader("ETag", ETag); List<string> rHeaders = request.Headers.AllKeys.ToList<string>(); if (rHeaders.Contains("if-none-match") && request.Headers["if-none-match"].IndexOf(ETag) >= 0) { response.StatusCode = 304; response.StatusDescription = "Not Modified"; sendBuffer = false; } try { if (sendBuffer) { response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; response.OutputStream.Write(buffer, 0, buffer.Length); } } catch (Exception ex) { MainConsole.Instance.Warn("[HTTPD]: Error - " + ex); } finally { //response.OutputStream.Close(); try { response.OutputStream.Flush(); response.Send(); //if (!response.KeepAlive && response.ReuseContext) // response.FreeContext(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } catch (Exception e) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: New Error in HTTP Server sending: " + e); } } }
private byte[] ProcessGetTexture(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { //MainConsole.Instance.DebugFormat("[GETTEXTURE]: called in {0}", m_scene.RegionInfo.RegionName); // Try to parse the texture ID from the request URL NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); string textureStr = query.GetOne("texture_id"); string format = query.GetOne("format"); if (m_assetService == null) { httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; return null; } UUID textureID; if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID)) { string[] formats; if (!string.IsNullOrEmpty(format)) { formats = new[] { format.ToLower() }; } else { formats = WebUtils.GetPreferredImageTypes(httpRequest.Headers.Get("Accept")); if (formats.Length == 0) formats = new[] { DefaultFormat }; // default } // OK, we have an array with preferred formats, possibly with only one entry httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; foreach (string f in formats) { if (FetchTexture(httpRequest, httpResponse, textureID, f)) break; } } else { MainConsole.Instance.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + httpRequest.Url); } httpResponse.Send(); httpRequest.InputStream.Close(); httpRequest = null; return null; }
/// <summary> /// This methods is the start of incoming HTTP request handling. /// </summary> /// <param name="request"></param> /// <param name="response"></param> public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response) { if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread { try { SendHTML500(response); } catch { } return; } string requestMethod = request.HttpMethod; string uriString = request.RawUrl; int requestStartTick = Environment.TickCount; // Will be adjusted later on. int requestEndTick = requestStartTick; IStreamedRequestHandler requestHandler = null; try { // OpenSim.Framework.WebUtil.OSHeaderRequestID // if (request.Headers["opensim-request-id"] != null) // reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]); //MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", true); // // This is the REST agent interface. We require an agent to properly identify // // itself. If the REST handler recognizes the prefix it will attempt to // // satisfy the request. If it is not recognizable, and no damage has occurred // // the request can be passed through to the other handlers. This is a low // // probability event; if a request is matched it is normally expected to be // // handled // IHttpAgentHandler agentHandler; // // if (TryGetAgentHandler(request, response, out agentHandler)) // { // if (HandleAgentRequest(agentHandler, request, response)) // { // requestEndTick = Environment.TickCount; // return; // } // } //response.KeepAlive = true; response.SendChunked = false; string path = request.RawUrl; string handlerKey = GetHandlerKey(request.HttpMethod, path); byte[] buffer = null; if (TryGetStreamHandler(handlerKey, out requestHandler)) { if (DebugLevel >= 3) LogIncomingToStreamHandler(request, requestHandler); response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. buffer = requestHandler.Handle(path, request.InputStream, request, response); } else { switch (request.ContentType) { case null: case "text/html": if (DebugLevel >= 3) LogIncomingToContentTypeHandler(request); buffer = HandleHTTPRequest(request, response); break; case "application/llsd+xml": case "application/xml+llsd": case "application/llsd+json": if (DebugLevel >= 3) LogIncomingToContentTypeHandler(request); buffer = HandleLLSDRequests(request, response); break; case "text/xml": case "application/xml": case "application/json": default: //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: in default handler"); // Point of note.. the DoWeHaveA methods check for an EXACT path // if (request.RawUrl.Contains("/CAPS/EQG")) // { // int i = 1; // } //MainConsole.Instance.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler"); if (DoWeHaveALLSDHandler(request.RawUrl)) { if (DebugLevel >= 3) LogIncomingToContentTypeHandler(request); buffer = HandleLLSDRequests(request, response); } else if (GetXmlRPCHandler(request.RawUrl) != null) { if (DebugLevel >= 3) LogIncomingToXmlRpcHandler(request); // generic login request. buffer = HandleXmlRpcRequests(request, response); } // MainConsole.Instance.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl); else if (DoWeHaveAHTTPHandler(request.RawUrl)) { if (DebugLevel >= 3) LogIncomingToContentTypeHandler(request); buffer = HandleHTTPRequest(request, response); } else { if (DebugLevel >= 3) LogIncomingToXmlRpcHandler(request); // generic login request. buffer = HandleXmlRpcRequests(request, response); } break; } } request.InputStream.Close(); if (buffer != null) { if (!response.SendChunked) response.ContentLength64 = buffer.LongLength; response.OutputStream.Write(buffer, 0, buffer.Length); } // Do not include the time taken to actually send the response to the caller in the measurement // time. This is to avoid logging when it's the client that is slow to process rather than the // server requestEndTick = Environment.TickCount; response.Send(); //response.OutputStream.Close(); //response.FreeContext(); } catch (SocketException e) { // At least on linux, it appears that if the client makes a request without requiring the response, // an unconnected socket exception is thrown when we close the response output stream. There's no // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore // the exception instead. // // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go // with the minimum first MainConsole.Instance.Warn(String.Format("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux ", e.Message), e); } catch (IOException e) { MainConsole.Instance.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); } catch (Exception e) { MainConsole.Instance.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); SendHTML500(response); } finally { // Every month or so this will wrap and give bad numbers, not really a problem // since its just for reporting int tickdiff = requestEndTick - requestStartTick; if (tickdiff > 3000 && requestHandler != null) { MainConsole.Instance.InfoFormat( "[BASE HTTP SERVER]: Slow handling of {0} {1} from {2} took {3}ms", requestMethod, uriString, request.RemoteIPEndPoint, tickdiff); } else if (DebugLevel >= 4) { MainConsole.Instance.DebugFormat( "[BASE HTTP SERVER]: HTTP IN :{0} took {1}ms", Port, tickdiff); } } }
internal void SendGenericHTTPResponse(Hashtable responsedata, OSHttpResponse response, OSHttpRequest request) { //MainConsole.Instance.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); byte[] buffer; if (responsedata.Count == 0) { response.StatusCode = (int)HttpStatusCode.NotFound; buffer = Encoding.UTF8.GetBytes("404"); response.OutputStream.Write(buffer, 0, buffer.Length); response.Send(); return; } int responsecode = (int)responsedata["int_response_code"]; string responseString = (string)responsedata["str_response_string"]; string contentType = (string)responsedata["content_type"]; if (responsedata.ContainsKey("error_status_text")) response.StatusDescription = (string)responsedata["error_status_text"]; if (responsedata.ContainsKey("keepalive")) response.KeepAlive = (bool)responsedata["keepalive"]; response.ContentType = string.IsNullOrEmpty(contentType) ? "text/html" : contentType; response.StatusCode = responsecode; if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently) { response.RedirectLocation = (string)responsedata["str_redirect_location"]; response.StatusCode = responsecode; } if (contentType != null && !(contentType.Contains("image") || contentType.Contains("x-shockwave-flash") || contentType.Contains("application/x-oar") || contentType.Contains("application/vnd.ll.mesh"))) { // Text buffer = Encoding.UTF8.GetBytes(responseString); } else { // Binary! buffer = Convert.FromBase64String(responseString); } bool sendBuffer = true; string ETag = Util.SHA1Hash(buffer.ToString()); response.AddHeader("ETag", ETag); List<string> rHeaders = request.Headers.AllKeys.ToList(); if (rHeaders.Contains("if-none-match") && request.Headers["if-none-match"].IndexOf(ETag) >= 0) { response.StatusCode = 304; response.StatusDescription = "Not Modified"; sendBuffer = false; } try { if (sendBuffer) { response.ContentEncoding = Encoding.UTF8; response.OutputStream.Write(buffer, 0, buffer.Length); } } catch (Exception ex) { MainConsole.Instance.Warn("[HTTPD]: Error - " + ex); } finally { try { response.Send(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Debug("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } } }
private void HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) { string requestBody; using(StreamReader reader = new StreamReader(request.InputStream, Encoding.UTF8)) requestBody = reader.ReadToEnd(); response.KeepAlive = true; response.ContentEncoding = Encoding.UTF8; OSD llsdRequest = null, llsdResponse = null; bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest")); if (requestBody.Length == 0) requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>"; try { llsdRequest = OSDParser.Deserialize(requestBody); } catch (Exception ex) { MainConsole.Instance.Warn("[BASE HTTP SERVER]: Error - " + ex); } if (llsdRequest != null) { LLSDMethod llsdhandler = null; if (_server.TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV) { // we found a registered llsd handler to service this request llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint); } else { // we didn't find a registered llsd handler to service this request // .. give em the failed message llsdResponse = GenerateNoLLSDHandlerResponse(); } } else llsdResponse = GenerateNoLLSDHandlerResponse(); byte[] buffer = new byte[0]; if (llsdResponse.ToString() == "shutdown404!") { response.ContentType = "text/plain"; response.StatusCode = (int)HttpStatusCode.NotFound; response.StatusDescription = "Not Found"; buffer = Encoding.UTF8.GetBytes("Not found"); } else { // Select an appropriate response format buffer = BuildLLSDResponse(request, response, llsdResponse); } try { response.OutputStream.Write(buffer, 0, buffer.Length); response.Send(); } catch (IOException e) { MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: LLSD IOException {0}.", e); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: LLSD issue {0}.\nNOTE: this may be spurious on Linux.", e); } }
private static void HandleStreamHandler(OSHttpRequest request, OSHttpResponse response, string path, ref int respcontentLength, IStreamedRequestHandler requestHandler) { byte[] buffer = null; response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. try { IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response); } catch (Exception ex) { MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: HTTP handler threw an exception " + ex + "."); } if (request.InputStream != null) request.InputStream.Dispose(); if (buffer == null) { if (response.OutputStream.CanWrite) { response.StatusCode = (int)HttpStatusCode.InternalServerError; buffer = Encoding.UTF8.GetBytes("Internal Server Error"); } else return;//The handler took care of sending it for us } else if (buffer == MainServer.BadRequest) { response.StatusCode = (int)HttpStatusCode.BadRequest; buffer = Encoding.UTF8.GetBytes("Bad Request"); } respcontentLength = buffer.Length; try { if (buffer != MainServer.NoResponse) response.OutputStream.Write(buffer, 0, buffer.Length); response.Send(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (HttpListenerException) { MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: HTTP request abnormally terminated."); } catch (IOException e) { MainConsole.Instance.Debug("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } finally { buffer = null; } }
private void SendHTML500(OSHttpResponse response) { // I know this statuscode is dumb, but the client/MSIE doesn't respond to 404s and 500s response.StatusCode = (int)HttpStatusCode.OK; response.AddHeader("Content-type", "text/html"); byte[] buffer = Encoding.UTF8.GetBytes(GetHTTP500()); response.ContentEncoding = Encoding.UTF8; try { response.OutputStream.Write(buffer, 0, buffer.Length); response.Send(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } }
/// <summary> /// Try all the registered xmlrpc handlers when an xmlrpc request is received. /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method. /// </summary> /// <param name="request"></param> /// <param name="response"></param> private bool HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) { XmlRpcRequest xmlRprcRequest = null; string requestBody; using (StreamReader reader = new StreamReader(request.InputStream, Encoding.UTF8)) requestBody = reader.ReadToEnd(); requestBody = requestBody.Replace("<base64></base64>", ""); try { if (requestBody.StartsWith("<?xml")) xmlRprcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody); } catch (XmlException) { } if (xmlRprcRequest != null) { string methodName = xmlRprcRequest.MethodName; byte[] buf; if (methodName != null) { xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); XmlRpcResponse xmlRpcResponse; XmlRpcMethod method; if (_server.TryGetXMLHandler(methodName, out method)) { xmlRprcRequest.Params.Add(request.Url); // Param[2] xmlRprcRequest.Params.Add(request.Headers.Get("X-Forwarded-For")); // Param[3] try { xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint); } catch (Exception e) { MainConsole.Instance.ErrorFormat("[BASE HTTP SERVER]: Requested method [{0}] from {1} threw exception: {2} {3}", methodName, request.RemoteIPEndPoint.Address, e, e.StackTrace); // if the registered XmlRpc method threw an exception, we pass a fault-code along xmlRpcResponse = new XmlRpcResponse(); // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php xmlRpcResponse.SetFault(-32603, string.Format("Requested method [{0}] from {1} threw exception: {2} {3}", methodName, request.RemoteIPEndPoint.Address, e, e.StackTrace)); } // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here response.KeepAlive = _server.GetXMLHandlerIsKeepAlive(methodName); } else { xmlRpcResponse = new XmlRpcResponse(); // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php xmlRpcResponse.SetFault( XmlRpcErrorCodes.SERVER_ERROR_METHOD, String.Format("Requested method [{0}] not found", methodName)); } buf = Encoding.UTF8.GetBytes(XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse)); response.ContentType = "text/xml"; } else { response.ContentType = "text/plain"; response.StatusCode = (int)HttpStatusCode.NotFound; response.StatusDescription = "Not Found"; buf = Encoding.UTF8.GetBytes("Not found"); response.ContentEncoding = Encoding.UTF8; } try { response.OutputStream.Write(buf, 0, buf.Length); response.Send(); } catch (SocketException e) { // This has to be here to prevent a Linux/Mono crash MainConsole.Instance.WarnFormat("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); } catch (IOException e) { MainConsole.Instance.Debug("[BASE HTTP SERVER]: XmlRpcRequest issue: " + e); } return true; } return false; }
/// <summary> /// FIXME: This should be part of BaseHttpServer /// </summary> internal static void DoHTTPGruntWork(BaseHttpServer server, PollServiceHttpRequest req, Hashtable responsedata) { OSHttpResponse response = new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request), req.HttpContext); byte[] buffer = server.DoHTTPGruntWork(responsedata, response); response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; try { response.OutputStream.Write(buffer, 0, buffer.Length); } catch (Exception ex) { m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex)); } finally { //response.OutputStream.Close(); try { response.OutputStream.Flush(); response.Send(); //if (!response.KeepAlive && response.ReuseContext) // response.FreeContext(); } catch (Exception e) { m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e)); } } }
private byte[] ProcessAvatarPickerSearch(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); string amt = query.GetOne("page-size"); string name = query.GetOne("names"); List<UserAccount> accounts = m_service.Registry.RequestModuleInterface<IUserAccountService>().GetUserAccounts(UUID.Zero, name) ?? new List<UserAccount>(0); OSDMap body = new OSDMap(); OSDArray array = new OSDArray(); foreach (UserAccount account in accounts) { OSDMap map = new OSDMap(); map["agent_id"] = account.PrincipalID; IUserProfileInfo profileInfo = DataManager.RequestPlugin<IProfileConnector>().GetUserProfile(account.PrincipalID); map["display_name"] = (profileInfo == null || profileInfo.DisplayName == "") ? account.Name : profileInfo.DisplayName; map["username"] = account.Name; array.Add(map); } body["agents"] = array; byte[] m = OSDParser.SerializeLLSDXmlBytes(body); httpResponse.Body.Write(m, 0, m.Length); httpResponse.StatusCode = (int)System.Net.HttpStatusCode.OK; httpResponse.Send(); return null; }