Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

First attempt to provide Persistent URLs

Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484
10-23-2008 08:46
I had a chance to Play around in the Beta Grid today and hope that my results will prove useful to others, or at least stimulate some discussion.
My first mission was to try and work around the fact that URLs for HTTP-IN are transient to say the least, whilst avoiding the use of an external server to act as Dynamic DNS.

Working on the same basis as the older LSL based Servers, I have made the assumption that the UUID of the server remains constant even when the URL changes, and therefor, have used eMail to establish the initial handshake. The server polls for incoming requests every minuet but the requester only poles IF it has not established a valid URL. Once established, all subsequent communication may be event driven with llHTTPRequest and llHTTPResponse acting as the transport layer.

The Server Portion:

CODE

// Very Keynes
// October 2008
//
// InterObject HTTP Server Beta
//
// System Variables
//
string MyAdr;
string MyUrl;
string xPath;
string Query;
//
default
{
// If any event has occured that releases the URL we need to request a new one
changed(integer c){
if (c & (CHANGED_REGION | CHANGED_REGION_RESTART | CHANGED_TELEPORT))llRequestURL();}
//
// Initialise the server
//
state_entry(){
llSetTimerEvent(60.0);// Start Polling for URL requests Via eMail
// Inform Owner of my eMail Address to place into all Requester Objects
llOwnerSay(MyAdr = (string)llGetKey()+"@lsl.aditi.lindenlab.com");
if("" == MyUrl)llRequestURL();// if we don't have a valid URL Request one
}
touch_start(integer total_number)
{
llOwnerSay(MyAdr);
llOwnerSay(MyUrl);
}
//
// The Server Portion - Process HTTP Requests
//
http_request(key id, string method, string body)
{
//Parse the request Header
xPath = llGetHTTPHeader(id, "x-path-info");
Query = llGetHTTPHeader(id, "x-query-string");
// Process Our own URL Request
if (method == URL_REQUEST_GRANTED)
llOwnerSay(MyUrl = body);
else if (method == URL_REQUEST_DENIED)
llOwnerSay("Something went wrong, no url. " + body);
//
// Process Incomming HTTP Requests
//
else if (method == "DELETE")
{
// perform DELETE requests here
llHTTPResponse(id, 200, "Delete Requested -" +
" x-path-info " + xPath +
" x-query-string " + Query
);
}
else if (method == "GET")
{
// perform GET Requests here
llHTTPResponse(id, 200, "Get Requested -" +
" x-path-info " + xPath +
" x-query-string " + Query
);
}
else if (method == "POST")
{
// perform POST requests here
llHTTPResponse(id, 200, "Post Requested" +
" x-path-info " + xPath +
" x-query-string " + Query
);
}
else if (method == "PUT")
{
// perform PUT Requests here
llHTTPResponse(id, 200, "Put Requested" +
" x-path-info " + xPath +
" x-query-string " + Query
);
}
else
{
// Respond with an Error if Requested Method is not Supported
llHTTPResponse(id, 405, "Method unsupported");
}
}
//
// Process incomming eMail Requests
//
timer(){llGetNextEmail("", "");}
email(string time, string address, string subject, string body, integer remaining)
{
if(MyUrl != "")llEmail(address, MyUrl, ""); // Send Current URL if valid
else llEmail(address, "Error, no url.", ""); // else Send Error Message
if(remaining)llGetNextEmail("", ""); // request next in queue
}
}


and the Requester Portion:

CODE

// Very Keynes
// October 2008
//
// InterObject HTTP Requestor Beta
//
// Server / DNS Fixed Address - eMail
//
string address = "3d6304ab-6ea9-dc27-e230-e1082e3e28d0@lsl.aditi.lindenlab.com";
//
// System Variables
//
string ServerURL; //Current Server / DNS URL
//
default
{
// if we don't have a valid ServerURL then request a new Server URL
state_entry(){if("" == ServerURL)state URLRequest;}
//
// Normal Processing follows
//
touch_start(integer total_number)
{
// test code to demonstate system
llHTTPRequest(ServerURL,[HTTP_METHOD,"DELETE"],"");
llHTTPRequest(ServerURL,[HTTP_METHOD,"GET"],"");
llHTTPRequest(ServerURL,[HTTP_METHOD,"POST"],"");
llHTTPRequest(ServerURL,[HTTP_METHOD,"PUT"],"");
}

http_response(key request_id, integer status, list metadata, string body)
{
if(404 == status)state URLRequest;
llSay(0, body); // Say the results of the request
}

}

state URLRequest
{
state_entry()
{
llOwnerSay("Requesting URL");
llEmail(address,"URL Request","");
llSetTimerEvent(30.0);
}
timer(){llGetNextEmail("", "");}
email(string time, string address, string subject, string body, integer remaining)
{
ServerURL = subject;
llSetTimerEvent(0.0);
llOwnerSay("Ready");
state default;
}
}


Create the Server First then copy the eMail Address it reports and paste it into the address line of the Requester code.
Compile the requester code.
The requester will say that it is "Requesting URL", it may take a minute or 2 to establish and will return "Ready".
Once the Requester is ready touch it and it should say the results of the 3 HTTP Requests that it makes.

The requester, in theory, should work in the main grid against a server in The Beta Grid, but for some reason today the beta grid is not sending outbound emails. It all works fine within the beta grid though.

Hope it gives others a place to start playing with what I feel is a much needed functionality.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
10-23-2008 11:53
Nicely done!

Why do you re-query the server's URL when the client region's sim restarts? That shouldn't be necessary unless they are in the same region, should it?
Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484
10-23-2008 12:06
Yes, and Yes :)
If it is in the same region then it knows it will definitely need a new URL, if it is in a different Region it 'assumes' a rolling restart is in progress and at this stage just hopes that the server restarted first :)
I need to do some more brain storming on the whole issue of when to request an update without relying to much on timers or email loops in the requester. My Current thinking leans towards having a Primary and Secondary server, in separate regions, that use the email to synchronize with each other, as they have to keep poling for requests anyway, and have the requester try them both before giving up.

Of course the logical way is to test for a 5xx message in the response, and then request a new URL, but I am not sure if that is implemented yet and ran out of time before I could try it.

[edit]
I just did a quick test from LSLEditor to the server sitting on the Beta Grid, deliberately giving an invalid URL returns:

*** http_response(8c3bc803-bf62-4839-a317-7d8e01a674a3,404,,cap not found: 'df82fa81-759a-7f22-efb9-ca60b5beabcd'

So the Status will be 404 for an invalid URL, I will Update the requester to Handle it.

Here is a short piece of test code that runs in LSLEditor against the server, not sure how long it will be before the url changes but anyone wants to run a test go ahead :)

CODE

string ServerURL = "http://sim3017.aditi.lindenlab.com:12046/cap/df82fa81-759a-7f22-efb9-ca60b5be7134";

default
{
touch_start(integer total_number)
{
llHTTPRequest(ServerURL+"/SQL/ADMIN?name=very", [HTTP_METHOD,"GET"], "");
}
http_response(key request_id, integer status, list metadata, string body)
{
llOwnerSay(body);
}
}
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
10-23-2008 14:31
Nice. Yeah, I was thinking about the possibility of using the status code. That's kind of bad news about the 404. The script should be able to use trailing path components too, and using 404 for "not found" responses from the script itself would be nice. It would be good to be able to distinguish between the server object no longer being available (at that URL) and that kind of script response without additional queries. Though this way does make sense given that the object's URL is distinguished by path part rather than domain name. Hmm....

I wish I knew why they didn't just allow any object to be contacted by key. They already have to lookup an object's key on the global grid for e-mail. I suppose you'd have to figure out which script in a prim/object would get the request though. Maybe http://server.secondlife.com/prim-key/encoded-script-name or something? Heh. Probably a bit late for that kind of suggestion anyway.
Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484
10-23-2008 16:49
They do allow use of the trailing path components and even provide a function to parse them, that is why I was expecting a 5xx code rather than a 404, but it is early days and not cast in stone, so Kelly may change it.

The test code returns
new: Get Requested - x-path-info /SQL/ADMIN x-query-string name=very

But I am happy to have the functionality as is, it is still a vast improvement on RPC or eMail alone.
Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484
10-24-2008 01:09
I have Updated the test scripts so that the requester ignores restarts but requests a url if the existing url is empty or the server returns a 404 error.
The server will now llOwnerSay its current eMail address and URL on touch to assist in testing.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
10-27-2008 10:19
I just created a JIRA requesting stable script URLs, with the proposed solution I mentioned above. See http://jira.secondlife.com/browse/SVC-3331

Your comments and/or vote would be greatly appreciated.