LSL HTTP server/examples/utility script

From Second Life Wiki
Jump to navigation Jump to search

This is a basic HTTP server utility script. The idea is that even though it looks complicated, for simple things you can just set a couple of variables and fill in a couple of lines in the GET or PUT handlers and be done. It sacrifices speed and memory in an attempt at ease of use, and probably misses the mark there as well.

TO USE: Look for the "put your code here" lines and return the text you want sent out.

If you modify the script on this wiki page, extend the first comment with a line like: // Random Avatar: Added object scanning

// This code was written by Kelly Linden
// This code is:
// * free to use or copy
// * free to modify or mangle or quote or crib from
// * free to distribute however you like
// There is NO warranty express or implied.

//////////////
// Settings //
//////////////
integer DEBUG = TRUE;      // Spam header information
integer USE_SSL = FALSE;    // Use https urls
integer SAY_URL = TRUE;     // Say the URL when granted.
integer GET_AGENTS = TRUE; // Will populate the AGENTS list with nearby agents

// Specify an url to register this url at
// Substitutes <URL> for the granted URL
// ex. "http://my_lsl_url.com/foo?url=<URL>&secret=bar"
string REGISTRATION_URL = "";

///////////////////////
// Special Variables //
///////////////////////
// These may have data in them when you have GET or PUT requests to handle.
list AGENTS;    // A list of agent name's w/in 96m and over this parcel.

//////////////////////
// Request handling //
//////////////////////
// Process a GET request.
// request_id: use to get headers and in llHTTPResponse
// path: A list of path segments past the base URL
// args: A list of query args like ["key1", "value1", "key2", "value2"]
string GET(key request_id, list path, list args)
{
    // Put your code here.

    // Return the body of your response.
    return "Hello World!";
}

// Process a POST request.
// request_id: use to get headers and in llHTTPResponse
// path: A list of path segments past the base URL
// args: A list of query args like ["key1", "value1", "key2", "value2"]
// body: The request body.
string POST(key request_id, list path, list args, string body)
{
    // Put your code here.
    
    // Return the body of your response.
    return "";
}

// For the sake of simplicity this script does not handle PUT or DELETE
// If needed these can be implemented similar to GET and POST

///////////////////////////////////////////////
// Dirty Details.                            //
///////////////////////////////////////////////

// This is a magic number for the changed flags
// that indicate we need a new URL.
// (CHANGED_REGION | CHANGED_TELEPORT | CHANGED_REGION_START)
integer CHANGED_URL = 1792;

// This will register with services that use fairly
// simple GET requests.
// If your service requires a POST or something fancy
// then you may need to rewrite this.
_REGISTER_URL(string url)
{
    if (SAY_URL) llOwnerSay(url);
    
    if (REGISTRATION_URL == "") return;
    
    list l = llParseString2List(REGISTRATION_URL,["<URL>"],[]);
    integer length = llGetListLength(l);
    string r;
    if (length == 0)
    {
        return;
    }
    else if (length == 1)
    {
        // No <URL> so just append.
        r = REGISTRATION_URL + url;
    }
    else
    {
        // just replace the first instance of <URL>
        r = llList2String(l,0) + url + llList2String(l,1);
    }
    
    llHTTPRequest(r,[],"");
}

// Spam headers and free memory with every request
_DEBUG(key id, string method, string body)
{
    if (!DEBUG) return;
    
    llOwnerSay(method + ": " + body);
    list headers = ["x-script-url","x-path-info","x-query-string","x-remote-ip","user-agent"];
    integer i;
    for (i=0;i<5;++i)
    {
        llOwnerSay(llList2String(headers,i) + ": " + llGetHTTPHeader(id,llList2String(headers,i)));
    }
    llOwnerSay("Script Memory Available: " + (string)(llGetFreeMemory() / 1024) + "k.");
}

// Request an url.
_REQUEST_URL()
{
    if (USE_SSL)
        llRequestSecureURL();
    else
        llRequestURL();
}

default 
{
    state_entry() 
    { 
        _REQUEST_URL();
        if(GET_AGENTS)
        {
            llSensor("",NULL_KEY,AGENT,96,PI); // Get some results immediately
            llSensorRepeat("",NULL_KEY,AGENT,96,PI,60); // Update the list every minute.
        }
    }
    on_rez(integer n) { _REQUEST_URL(); }
    changed(integer c) { if (c & (CHANGED_URL) ) _REQUEST_URL(); }
 
    // Handle an HTTP request
    http_request(key id, string method, string body)
    {
        // Spam debug info
        _DEBUG(id, method, body);
        
        // We got an URL
        if (method == URL_REQUEST_GRANTED)
        {
            if (DEBUG) llOwnerSay("Free URLs: " + (string)llGetFreeURLs());
            _REGISTER_URL(body);
            return;
        }
        
        // There was an error getting an URL
        // IM the object owner.
        else if (method == URL_REQUEST_DENIED)
        {
            llInstantMessage(llGetOwner(),"Failed to get url. " 
                + (string)llGetFreeURLs() + " URLs free.  Reason: " + body);
            return;
        }
        
        // The GET or POST method results will go in here.
        string response = "";
        
        // Split up the query args into a usable list
        // If you use ?, =, + or & in keys or values then you may need to adjust this.
        list query_args = llParseString2List(llGetHTTPHeader(id,"x-query-string"),["?","=","+","&"],[]);
        
        // Split up any path information into path segments
        list path = llParseString2List(llGetHTTPHeader(id,"x-path-info"),["/"],[]);
        
        // This was a GET request, hand off to the GET handler.
        if (method == "GET")
        {
            response = GET(id,path,query_args);
        }
        
        // This was a POST request, hand off to the POST handler.
        else if (method == "POST")
        {
            response = POST(id,path,query_args,body);
        }
        
        // If we have a response, status is 200 OK
        if (response != "") 
        {
            llHTTPResponse(id,200,response);
        }
        
        // No response so something went wrong.
        else
        {
            llHTTPResponse(id,400,"Error processing request.");
        }
    }
    
    sensor(integer n)
    {
        key p = llList2Key(llGetParcelDetails(llGetPos(),[PARCEL_DETAILS_OWNER]),0);
        AGENTS = [];
        integer i;
        for(i=0;i<n;++i)
        {
            if (p == llList2Key(llGetParcelDetails(llDetectedPos(i),[PARCEL_DETAILS_OWNER]),0))
                AGENTS += llDetectedName(i);
        }
    }
    
    no_sensor()
    {
        AGENTS = [];
    }
}