public Hashtable GetEvents(AsyncHttpRequest pRequest)
            {
                Hashtable responsedata = new Hashtable();

                lock (m_eqLock) // should already be locked on entry to this function
                {
                    // If the queue is gone we're being cleaned up.  Send a 404
                    if (isQueueValid == false)
                    {
                        responsedata["int_response_code"] = 404;
                        responsedata["content_type"] = "text/plain";
                        responsedata["str_response_string"] = "Not Found";
                        responsedata["error_status_text"] = "Not Found";

                        return responsedata;
                    }

                    if (m_highPriorityItems.Count == 0 && m_Items.Count == 0)
                    {
                        /*
                        ** The format of this response is important. If its not setup this way the client will
                        ** stop polling for new events.  http://wiki.secondlife.com/wiki/EventQueueGet
                        */
                        responsedata["int_response_code"] = 502;
                        responsedata["content_type"] = "text/plain";
                        responsedata["str_response_string"] = "Upstream error: ";
                        responsedata["error_status_text"] = "Upstream error:";
                        responsedata["http_protocol_version"] = "1.0";

                        if (DebugLevel > 0)
                        {
                            m_log.DebugFormat(
                                "[EVENT QUEUE GET MODULE]: Eq TIMEOUT/502 to client {0} in region {1}",
                                m_agentID, 
                                m_Caps.RegionName);
                        }

                        return responsedata;
                    }

                    // Return the events we have queued
                    OSDArray array = new OSDArray();

                    //if there are no high priority items, then dequeue the normal
                    //priority items
                    if (!DequeueIntoArray(m_highPriorityItems, array))
                    {
                        DequeueIntoArray(m_Items, array);
                    }

                    // m_log.ErrorFormat("[EVENTQUEUE]: Responding with {0} events to viewer.", array.Count.ToString());

                    OSDMap events = new OSDMap();
                    events.Add("events", array);
                    events.Add("id", new OSDInteger(m_SequenceNumber));

                    responsedata["int_response_code"] = 200;
                    responsedata["content_type"] = "application/xml";
                    var eventData = OSDParser.SerializeLLSDXmlString(events);
                    responsedata["str_response_string"] = eventData;

                    //warn at 512kB of uncompressed data
                    const int WARN_THRESHOLD = 524288;
                    if (eventData.Length > WARN_THRESHOLD)
                    {
                        m_log.WarnFormat("[EVENTQUEUE]: Very large message being enqueued. Size: {0}, Items{1}. Contents: ",
                            eventData.Length, array.Count);

                        foreach (var e in array)
                        {
                            OSDMap evt = (OSDMap)e;
                            m_log.WarnFormat("[EVENTQUEUE]: {0}", evt["message"]);
                        }
                    }

                    return responsedata;
                }
            }
            public void Enqueue(OSD ev, bool highPriority)
            {
                AsyncHttpRequest pendingRequest = null;
                Hashtable responseToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == false)
                    {
                        m_log.ErrorFormat("[EVENTQUEUE]: Unable to enqueue new event. isQueueValid == false");
                        return;
                    }

                    if (highPriority)
                    {
                        m_highPriorityItems.Enqueue(ev);
                    }
                    else
                    {
                        m_Items.Enqueue(ev);
                    }

                    if (m_Request != null)
                    {
                        pendingRequest = m_Request;
                        m_Request = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
            public void DumpQueue()
            {
                AsyncHttpRequest pendingRequest = null;
                Hashtable responseToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == true)
                    {
                        m_highPriorityItems.Clear();
                        m_highPriorityItems = null;
                        m_Items.Clear();
                        m_Items = null;
                    }

                    // If a request is pending signal it.  The response handler will send a 404
                    if (m_Request != null)
                    {
                        pendingRequest = m_Request;
                        m_Request = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
            private void TimeoutHandler(AsyncHttpRequest pRequest)
            {
                UUID sessionid = pRequest.AgentID;
                Hashtable eventsToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == false)
                        return;

                    if (m_Request == pRequest)
                    {
                        m_Request = null;
                        eventsToSend = GetEvents(pRequest);
                    }
                }

                if (eventsToSend != null)
                    pRequest.SendResponse(eventsToSend);
            }
            public void Handle(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
            {
                AsyncHttpRequest pendingRequest = null;
                AsyncHttpRequest eventQueueRequest = 
                    new AsyncHttpRequest(server, httpRequest, httpResponse, m_agentID, TimeoutHandler, m_timeout);
                Hashtable responseToSend = null;

                lock (m_eqLock)
                {
                    if (!isQueueValid)
                    {
                        m_log.ErrorFormat("[EVENTQUEUE]: HandleRequest for {0} failed, isQueueValid == false", m_agentID);
                        m_Request = null;
                        return;
                    }

                    pendingRequest = m_Request;
                    m_Request = eventQueueRequest;

                    if ((pendingRequest == null) && (m_highPriorityItems.Count > 0 || m_Items.Count > 0))
                    {
                        pendingRequest = m_Request;
                        m_Request = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
Exemple #6
0
 private void TimeoutHandler(AsyncHttpRequest pRequest)
 {
     pRequest.SendResponse(ProcessEvents(pRequest, true));
 }
Exemple #7
0
        private Hashtable ProcessEvents(AsyncHttpRequest pRequest, bool timedOut)
        {
            UUID requestID = pRequest.RequestID;
            UrlData urlData = null;
            RequestData requestData = null;
            Hashtable response = new Hashtable();

            response["content_type"] = "text/plain";

            lock (m_RequestMap)
            {
                if (m_RequestMap.TryGetValue(requestID, out requestData))
                    m_RequestMap.Remove(requestID);
            }

            if (requestData != null)
            {
                string url = URLFromURI(requestData.uri);

                lock (m_UrlMap)
                {
                    if (m_UrlMap.TryGetValue(url, out urlData))
                        urlData.requests.Remove(requestID);
                }
            }

            if ((requestData == null) || (urlData == null))
            {
                response["int_response_code"] = 404;
                response["str_response_string"] = "Request not found";
                return response;
            }

            if ((timedOut == true) ||
                ((requestData != null) && (requestData.requestDone == false)))
            {
                response["int_response_code"] = 500;
                response["str_response_string"] = "Script timeout";
            }
            else
            {
                //put response
                response["int_response_code"] = requestData.responseCode;
                response["str_response_string"] = requestData.responseBody;
                response["content_type"] = requestData.contentType;
            }

            return response;
        }
Exemple #8
0
        private Hashtable ProcessEvents(AsyncHttpRequest pRequest)
        {
            UUID sessionID = pRequest.AgentID;

            // Is there data to send back?  Then send it.
            if (HasEvents(pRequest.RequestID, sessionID))
                return (GetEvents(pRequest.RequestID, sessionID));
            else
                return (NoEvents(pRequest.RequestID, sessionID));
        }
Exemple #9
0
        public void AsyncHttpRequest(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            UUID urlcode;
            if (UUID.TryParse(path, out urlcode))
                return;

            AsyncHttpRequest asyncRequest = 
                new AsyncHttpRequest(server, httpRequest, httpResponse, urlcode, TimeoutHandler, m_DefaultTimeout);

            UUID requestID = asyncRequest.RequestID;
            Hashtable request = asyncRequest.RequestData;

            string uri = request["uri"].ToString();
            bool is_ssl = uri.Contains("lslhttps");

            try
            {
                Hashtable headers = (Hashtable)request["headers"];
                //HTTP server code doesn't provide us with QueryStrings
                string queryString = "";

                int pos1 = uri.IndexOf("/");// /lslhttp
                int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/
                int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/
                string pathInfo = uri.Substring(pos3);

                string url = URLFromURI(uri);
                UrlData urlData = null;

                lock (m_UrlMap)
                {
                    m_UrlMap.TryGetValue(url, out urlData);

                    // Returning NULL sends a 404 from the base server
                    if (urlData == null)
                        asyncRequest.SendResponse(server.SendHTML404(httpResponse));
                        
                }

                //for llGetHttpHeader support we need to store original URI here
                //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers 
                //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader

                RequestData requestData =
                    new RequestData
                    {
                        requestId = asyncRequest.RequestID,
                        polledRequest = asyncRequest,
                        requestDone = false,
                        startTime = Environment.TickCount,
                        uri = uri
                    };

                if (requestData.headers == null)
                    requestData.headers = new Dictionary<string, string>();

                // Copy in the headers, convert keys to lower case: See.
                // http://wiki.secondlife.com/wiki/LlGetHTTPHeader
                foreach (DictionaryEntry header in headers)
                {
                    string key = (string)header.Key;
                    string value = (string)header.Value;
                    requestData.headers.Add(key.ToLower(), value);
                }

                foreach (DictionaryEntry de in request)
                {
                    if (de.Key.ToString() == "querystringkeys")
                    {
                        String[] keys = (String[])de.Value;
                        foreach (String key in keys)
                        {
                            if (request.ContainsKey(key))
                            {
                                string val = (String)request[key];
                                queryString = queryString + key + "=" + val + "&";
                            }
                        }

                        if (queryString.Length > 1)
                            queryString = queryString.Substring(0, queryString.Length - 1);
                    }
                }

                //if this machine is behind DNAT/port forwarding, currently this is being
                //set to address of port forwarding router
                requestData.headers["x-remote-ip"] = httpRequest.RemoteIPEndPoint.ToString();
                requestData.headers["x-path-info"] = pathInfo;
                requestData.headers["x-query-string"] = queryString;
                requestData.headers["x-script-url"] = urlData.url;

                lock (m_RequestMap)
                {
                    m_RequestMap.Add(requestID, requestData);
                }

                lock (m_UrlMap)
                {
                    urlData.requests.Add(requestID);
                }

                urlData.engine.PostScriptEvent(urlData.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); 
            }
            catch (Exception we)
            {
                m_log.Warn("[HttpRequestHandler]: http-in request failed");
                m_log.Warn(we.Message);
                m_log.Warn(we.StackTrace);

                asyncRequest.SendResponse(server.SendHTML500(httpResponse));
            }
        }
Exemple #10
0
        private void TimeoutHandler(AsyncHttpRequest pRequest)
        {
            UUID sessionid = pRequest.AgentID;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionid, out connection);
                if (connection == null)
                    return;
                if (connection.request == pRequest)
                    connection.request = null;
            }

            pRequest.SendResponse(ProcessEvents(pRequest));
        }
Exemple #11
0
        public void AsyncReadResponses(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            int pos1 = path.IndexOf("/");               // /ReadResponses
            int pos2 = path.IndexOf("/", pos1 + 1);     // /ReadResponses/
            int pos3 = path.IndexOf("/", pos2 + 1);     // /ReadResponses/<UUID>/
            int len = pos3 - pos2 - 1;
            string uri_tmp = path.Substring(pos2 + 1, len);

            var authHeader = httpRequest.Headers.Get("Authorization");
            if (authHeader != null)
            {
                if (!authHeader.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
                {
                    m_log.Warn($"[REMOTECONSOLE] ReadResponses JWT Authorization header format failure from '{httpRequest.RemoteIPEndPoint}'.");
                    return;
                }

                try
                {
                    var token = new JWToken(authHeader.Substring(7), m_sigUtil);

                    // TODO: Make the scope strings come from some central list that can be registered into?
                    if (!(token.HasValidSignature && token.IsNotExpired && token.Payload.Scope == "remote-console"))
                    {
                        m_log.Warn($"[REMOTECONSOLE] ReadResponses invalid/expired/wrong scope JWToken from '{httpRequest.RemoteIPEndPoint}'.");
                        return;
                    }

                    m_log.Info($"[REMOTECONSOLE] ReadResponses for session '{uri_tmp}' accessed via JWT by '{token.Payload.Username}' from '{httpRequest.RemoteIPEndPoint}'.");
                }
                catch (JWTokenException jte)
                {
                    m_log.Error($"[REMOTECONSOLE] Failure with JWToken in ReadResponses from '{httpRequest.RemoteIPEndPoint}': {jte}");
                    return;
                }
            }
            else
            {
                m_log.Warn($"[REMOTECONSOLE] ReadResponses for session '{uri_tmp}' from '{httpRequest.RemoteIPEndPoint}' being accessed without Authorization header!");
            }
            // BUG: Longstanding issue: if someone gets ahold of, or guesses, the ID of another user they can send comamnds to the console.
            // The only way I can think to close this bug is to associate each session with something the user cannot change. Not sure, but maybe the IP address of the connection would work?

            UUID sessionID;
            if (UUID.TryParse(uri_tmp, out sessionID) == false)
                return;

            // Create the new request
            AsyncHttpRequest newRequest = 
                new AsyncHttpRequest(server, httpRequest, httpResponse, sessionID, TimeoutHandler, 60 * 1000);
            AsyncHttpRequest currentRequest = null;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionID, out connection);
                if (connection == null)
                    return;

                currentRequest = connection.request;
                connection.request = newRequest;
            }

            // If there was a request already posted, signal it.
            if (currentRequest != null)
            {
                currentRequest.SendResponse(ProcessEvents(currentRequest));
            }
        }
Exemple #12
0
        public void AsyncReadResponses(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            int pos1 = path.IndexOf("/");               // /ReadResponses
            int pos2 = path.IndexOf("/", pos1 + 1);     // /ReadResponses/
            int pos3 = path.IndexOf("/", pos2 + 1);     // /ReadResponses/<UUID>/
            int len = pos3 - pos2 - 1;
            string uri_tmp = path.Substring(pos2 + 1, len);

            UUID sessionID;
            if (UUID.TryParse(uri_tmp, out sessionID) == false)
                return;

            // Create the new request
            AsyncHttpRequest newRequest = 
                new AsyncHttpRequest(server, httpRequest, httpResponse, sessionID, TimeoutHandler, 60 * 1000);
            AsyncHttpRequest currentRequest = null;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionID, out connection);
                if (connection == null)
                    return;

                currentRequest = connection.request;
                connection.request = newRequest;
            }

            // If there was a request already posted signal it
            if (currentRequest != null)
            {
                currentRequest.SendResponse(ProcessEvents(currentRequest));
            }
        }
            private void SendFetchTimeout(AsyncHttpRequest pRequest, ulong age)
            {
                var timeout = new Hashtable();
                timeout["response_binary"] = new byte[0];
                timeout["int_response_code"] = 504; //gateway timeout

                m_log.WarnFormat("[CAPS/INVENTORY]: HandleAsyncFetchInventoryDescendents: Request was too old to be processed {0}ms for {1}", age, m_agentID);

                pRequest.SendResponse(timeout);
            }
            private void AsyncFetchInventoryDescendents(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
            {
                AsyncHttpRequest request = new AsyncHttpRequest(server, httpRequest, httpResponse, m_agentID, null, 0);

                m_inventoryPool.QueueWorkItem((AsyncHttpRequest req) =>
                    {
                        try
                        {
                            ulong age = Util.GetLongTickCount() - req.RequestTime;
                            if (age >= INVENTORY_FETCH_TIMEOUT)
                            {
                                SendFetchTimeout(req, age);
                                return;
                            }

                            byte[] ret = this.FetchInventoryDescendentsRequest((string)req.RequestData["body"],
                                (string)req.RequestData["uri"], String.Empty, req.HttpRequest, req.HttpResponse);

                            var respData = new Hashtable();
                            respData["response_binary"] = ret;
                            respData["int_response_code"] = 200;
                            respData["content_type"] = "application/llsd+xml";

                            req.SendResponse(respData);
                        }
                        catch (Exception e)
                        {
                            m_log.ErrorFormat("[CAPS/INVENTORY]: HandleAsyncFetchInventoryDescendents threw an exception: {0}", e);
                            var respData = new Hashtable();
                            respData["response_binary"] = new byte[0];
                            respData["int_response_code"] = 500;
                            req.SendResponse(respData);
                        }
                    },

                    request
                );
            }