Difference between revisions of "User:Void Singer/Teacup"
Void Singer (talk | contribs) m (new page) |
Void Singer (talk | contribs) (And now we haz content!) |
||
Line 5: | Line 5: | ||
{{void-box | {{void-box | ||
|title= | |title=Teacup | ||
|content= | |content= | ||
=== stuff: === | === What is it? === | ||
Teapot is an open source webserver front end for LSL's HTTP-in [[Media On A Prim|MOAP]] functionality. It builds on the work of Several other people including but not limited to, Kelly Linden, Torley Linden, Tali Rosca, Vegas Silverweb, and special mentions to X and/or Y Linden (whoever wrote the original align for LSL). The idea is to make it easy to serve web content from within SL, with a minimum of work or understanding, in the most standards compliant and flexible way possible. | |||
=== How does it work? === | |||
When Teacup Server starts, it requests a URL from the region server, and when it gets a response, sets the prim side 0 face to a "data:" URN wrapped page (limiting the use of the slow ll*PrimMedia* cotrols to once at load). If the response was "no URL", then it sets an error page. If the response was a new URL, then the server test page is set, and that also requests and loads the javascript library. The first time the javascript library loads, it attempts to load the "index.tsp" page by creating a script element container for it. The server gets the request for the new page, and asks the file server for it... if the file server doesn't have it, or isn't present, the an error code will be sent to the html page, which stays where it was and pops up an error alert. If the file was found, it's loaded to the script element. Once loaded, the javascript library grabs the contents and overwrites the body of the html page with them, then it checks each link on the new page, sets up a trap for any internal links leading to ".tsp" pages, and removes the script container from the page. If a trapped link is clicked, instead of loading directly, the javascript library catches the link, and repeats the same actions it used to load the "index.tsp" page. links to outside resources are not interfered with, and load normally. the page code is left untouched. | |||
=== Format Requirments? === | |||
Teacup serves a modified html page. | |||
* The file name should end with ".tsp" (Teacup server page) | |||
* The contents are limited to what's valid ''inside'' a normal html "body" tag. | |||
* It also requires the following minor changes, made in order: | |||
*# all backslash characters must be converted to double backslashes (may be applied on a line by line basis) | |||
*# all single quote characters must be prefixed with a backslash (may be applied on a line by line basis) | |||
*# all line breaks should be converted to "\n" literals (may be applied on a line by line basis) | |||
*# The page should be prefixed with "vTea='" and suffixed with "';" (applies only to the whole page) | |||
=== why all the tea jokes? === | |||
So why all the tea jokes? Well I happen to like steam punk stuff, and it has a sort of pseudo-victorian-era feel to it, but in all truth it was sparked by a bit of geek humor. There was an old april fool's joke passed around as an official sounding memo about creating a protocol for controlling coffee pots with http (back before they actually started making net enabled appliances) and part of that proposal was that teapots should return an error response "418 I'm a teapot" if you tried to make it brew coffee... since these servers are small, and 418 is safe to use internally because it wouldn't normally be encountered, I figured hey, Teapot --> Teacup --> Victorian Imagery --> Steam Punk --> teacup that is it's own teapot --> [[Server In a Prim|SIP]] --> many different types of tea --> Success! | |||
... And thus was born the SIP, Teapot server, and Red Tea file system | |||
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]] | |||
}} | |||
{{void-box | |||
|title=Code | |||
|content= | |||
<lsl>/*( Teacup Server v0.2 )*/ | |||
//-- stores the entire bootstrap or error page for touch handling | |||
string gStrAdr; | |||
//-- pending request queue for pages waiting to be served | |||
//-- this (hopefully) prevents reponding after SL send a timeout. | |||
list gLstPndKey; | |||
list gLstPndTim; | |||
default{ | |||
state_entry(){ | |||
//-- Request URL on start up | |||
llRequestURL(); | |||
} | |||
on_rez( integer vIntBgn ){ | |||
//-- Clear pending and request URL on rez | |||
gLstPndKey = gLstPndTim = []; | |||
llRequestURL(); | |||
} | |||
changed( integer vBitChg ){ | |||
if ((CHANGED_REGION_START | CHANGED_REGION) & vBitChg && 1){ | |||
//-- Clear pending and request URL on region restart, or region change | |||
gLstPndKey = gLstPndTim = []; | |||
llRequestURL(); | |||
} | |||
} | |||
touch_end( integer vIntTch ){ | |||
if (!llDetectedTouchFace( 0 )){ | |||
//-- Allow external load for those that don't have shared media, or have it disabled. | |||
llLoadURL( llDetectedKey( 0 ), "Teacup Page Loader for " + llGetObjectName(), gStrAdr ); | |||
} | |||
} | |||
http_request( key vKeySrc, string vStrMth, string vStrBdy ){ | |||
if (URL_REQUEST_GRANTED == vStrMth){ //-- Got a url, push the bootsrap page | |||
gStrAdr = "data:text/html;charset:utf-8, | |||
<html> | |||
<head> | |||
<base href='" + vStrBdy + "/'> | |||
<script type='text/javascript' src='teacup.js' defer='1'></script> | |||
</head> | |||
<body> | |||
<h1>Teacup Server Test Page</h1> | |||
<hr/> | |||
<p>ZOMG it works. Maybe you should add some tea leaves now?</p> | |||
<hr/> | |||
<p style='text-align:center;font-size:50%;'>Teacup v0.1 Server</a><br/><a href='secondlife:///app/agent/" + (string)llGetOwner() + "/about'>Contact Owner</a> | |||
</body> | |||
</html>"; | |||
llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, gStrAdr, PRIM_MEDIA_CURRENT_URL, gStrAdr] ); | |||
}else if (URL_REQUEST_DENIED == vStrMth){ //-- didn't get a url, push a warning page | |||
gStrAdr = "data:text/html;charset=utf-8, | |||
<html> | |||
<body> | |||
<h1 style='align: center;'>Unable to Generate URL interface</h1> | |||
<hr/> | |||
<p>Blah, this tastes like coffee!</p> | |||
<hr/> | |||
<p style='text-align:center;font-size:50%;'>Teacup v0.1 Server</a><br/><a href='secondlife:///app/agent/" + (string)llGetOwner() + "/about'>Contact Owner</a> | |||
</body> | |||
</html>"; | |||
llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, gStrAdr, PRIM_MEDIA_CURRENT_URL, gStrAdr] ); | |||
}else if ("GET" == vStrMth){ //-- got a page request | |||
//-- reuse the method string to see if it's a valid file path or the root | |||
//-- if the request has already timed out, it'll be blank and ignored | |||
if (vStrMth = llUnescapeURL( llDeleteSubString( llGetHTTPHeader( vKeySrc, "x-path-info" ), 0, 0 ) )){ | |||
if ("teacup.js" == vStrMth){ //-- if it's our bootsrap js page, we can go ahead and server that straight away | |||
llHTTPResponse( vKeySrc, 200, "/* teacup.js <!-- */ | |||
function addEventListener( instance, eventName, listener ){ | |||
var listenerFn = listener; | |||
if (instance.addEventListener){ | |||
instance.addEventListener( eventName, listenerFn, false ); | |||
}else if (instance.attachEvent){ | |||
listenerFn = function(){ listener( window.event ); } | |||
instance.attachEvent( 'on' + eventName, listenerFn ); | |||
} | |||
} | |||
function uBoil( vEvent ){ | |||
if (vEvent.preventDefault){ | |||
vEvent.preventDefault(); | |||
} | |||
if (typeof( this.href ) != 'undefined'){ | |||
uSteep( this.href ); | |||
}else{ | |||
uSteep( 'index.tsp' ); | |||
} | |||
} | |||
function uSteep( vPage ){ | |||
vTeabag = document.createElement( 'script' ); | |||
vTeabag.setAttribute( 'type', 'text/javascript' ); | |||
document.getElementsByTagName( 'head' ).item( 0 ).appendChild( vTeabag ); | |||
vEggTimer = setTimeout( 'uPour( \\'504 Gateway Timeout\\' )', 23000 ); | |||
addEventListener( vTeabag, 'load', uPour ); | |||
vTeabag.src = vPage; | |||
} | |||
function uPour( vErr ){ | |||
clearTimeout( vEggTimer ); | |||
if (typeof( vTea ) == 'undefined'){ | |||
if (typeof( vErr ) != 'text'){ | |||
vErr == '000 Malformed Page'; | |||
} | |||
alert( vErr + '\\n' + vTeabag.src.substring( 81 ) ); | |||
}else{ | |||
document.getElementsByTagName( 'body' ).item( 0 ).innerHTML = vTea; | |||
for (i = 0; i < document.links.length; i++){ | |||
if (~document.links[ i ].href.indexOf( '.tsp' )){ | |||
addEventListener( document.links[ i ], 'click', uBoil ); | |||
} | |||
} | |||
} | |||
document.getElementsByTagName( 'head' )[ 0 ].removeChild( vTeabag ); | |||
vTeabag = vTea = vEggTimer = (function(){})(); | |||
} | |||
uBoil( '' ); | |||
/* --> */" ); | |||
}else{ //-- request is for content | |||
//-- send a request for the content we want | |||
llMessageLinked( LINK_SET, 418, vStrMth, vKeySrc ); | |||
//-- store the request and a timeout value so we don't try to send pages after SL has already sent a timeout | |||
gLstPndKey += [vKeySrc]; | |||
gLstPndTim += [llGetUnixTime() + 20]; | |||
llSetTimerEvent( 3.0 ); | |||
} | |||
} | |||
} | |||
} | |||
link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){ | |||
//-- only accept return status codes we understand. | |||
if (~llListFindList( [200, 404], [vIntDta] )){ | |||
//-- is the request still waiting to be served? | |||
if (~vIntSrc = llListFindList( gLstPndKey, [vKeyDta] )){ | |||
//-- server the request, remove it from the pending queue | |||
llHTTPResponse( vKeyDta, vIntDta, vStrDta ); | |||
gLstPndKey = llDeleteSubList( gLstPndKey, vIntSrc, vIntSrc ); | |||
//-- reset the timer for the queue based on whether the queue is empty or not | |||
llSetTimerEvent( !!([] != gLstPndTim = llDeleteSubList( gLstPndTim, vIntSrc, vIntSrc )) * 3.0 ); | |||
} | |||
} | |||
} | |||
timer(){ | |||
if (gLstPndTim != []){ //-- are there items in the pending queue? | |||
while (llList2Integer( gLstPndTim, 0 ) < llGetUnixTime()){ | |||
//--loop through and remove expired pending requests | |||
gLstPndTim = llDeleteSubList( gLstPndTim, 0, 0 ); | |||
gLstPndKey = llDeleteSubList( gLstPndKey, 0, 0 ); | |||
if (gLstPndTim == []){ //-- if the queue is emptied, stop the timer and exit | |||
llSetTimerEvent( 0.0 ); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
/*//-- License Text --//*/ | |||
/*// Free to copy, use, modify, distribute, or sell, with attribution. //*/ | |||
/*// (C)2011 (CC-BY) [ http://creativecommons.org/licenses/by/3.0 ] //*/ | |||
/*// Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ] //*/ | |||
/*// All usages must contain a plain text copy of the previous 2 lines. //*/ | |||
/*//-- --//*/</lsl> | |||
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]] | |||
}} | |||
{{void-box | |||
|title=Notes | |||
|content= | |||
=== Compatible File Systems and Extensions === | |||
[[User:Void_Singer/Red_Tea|Red Tea]] - A very simple backend file sysytem for a starting point]. | |||
=== Known Bugs === | |||
* inline script/style elements are not evaluated at load time (inline attributes should work normally) | |||
** images have their own events, and can be used to trigger page load javascript | |||
* document/body onload events are not triggered for .tsp pages | |||
** images have their own events, and can be used to trigger page load javascript | |||
=== Planned Upgrades === | |||
* Move the test page and base javascript library out of the main script and into either the File System, or a separate load handler script | |||
* Add support for Site wide javascript and css or make them load normally to the page | |||
* Trigger at least the normal load events manually | |||
* find a convenient way to set the page title... probably use the page name | |||
=== NOTE TO WIKI EDITORS === | |||
I have kept this page as a sub page of my user page to denote that it should NOT be edited except to correct errors, add confirmed bugs, or add related resources. | |||
The intent is to retain executive control in one person's hands to retain a cohesive vision of it's development. | |||
If you have an improvement suggestion, or request, please use the [[User:Void Singer/Teacup|discussion]] page. | |||
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]] | |||
}} | }} |
Revision as of 02:11, 15 April 2011
Teacup
What is it?
Teapot is an open source webserver front end for LSL's HTTP-in MOAP functionality. It builds on the work of Several other people including but not limited to, Kelly Linden, Torley Linden, Tali Rosca, Vegas Silverweb, and special mentions to X and/or Y Linden (whoever wrote the original align for LSL). The idea is to make it easy to serve web content from within SL, with a minimum of work or understanding, in the most standards compliant and flexible way possible.
How does it work?
When Teacup Server starts, it requests a URL from the region server, and when it gets a response, sets the prim side 0 face to a "data:" URN wrapped page (limiting the use of the slow ll*PrimMedia* cotrols to once at load). If the response was "no URL", then it sets an error page. If the response was a new URL, then the server test page is set, and that also requests and loads the javascript library. The first time the javascript library loads, it attempts to load the "index.tsp" page by creating a script element container for it. The server gets the request for the new page, and asks the file server for it... if the file server doesn't have it, or isn't present, the an error code will be sent to the html page, which stays where it was and pops up an error alert. If the file was found, it's loaded to the script element. Once loaded, the javascript library grabs the contents and overwrites the body of the html page with them, then it checks each link on the new page, sets up a trap for any internal links leading to ".tsp" pages, and removes the script container from the page. If a trapped link is clicked, instead of loading directly, the javascript library catches the link, and repeats the same actions it used to load the "index.tsp" page. links to outside resources are not interfered with, and load normally. the page code is left untouched.
Format Requirments?
Teacup serves a modified html page.
- The file name should end with ".tsp" (Teacup server page)
- The contents are limited to what's valid inside a normal html "body" tag.
- It also requires the following minor changes, made in order:
- all backslash characters must be converted to double backslashes (may be applied on a line by line basis)
- all single quote characters must be prefixed with a backslash (may be applied on a line by line basis)
- all line breaks should be converted to "\n" literals (may be applied on a line by line basis)
- The page should be prefixed with "vTea='" and suffixed with "';" (applies only to the whole page)
why all the tea jokes?
So why all the tea jokes? Well I happen to like steam punk stuff, and it has a sort of pseudo-victorian-era feel to it, but in all truth it was sparked by a bit of geek humor. There was an old april fool's joke passed around as an official sounding memo about creating a protocol for controlling coffee pots with http (back before they actually started making net enabled appliances) and part of that proposal was that teapots should return an error response "418 I'm a teapot" if you tried to make it brew coffee... since these servers are small, and 418 is safe to use internally because it wouldn't normally be encountered, I figured hey, Teapot --> Teacup --> Victorian Imagery --> Steam Punk --> teacup that is it's own teapot --> SIP --> many different types of tea --> Success!
... And thus was born the SIP, Teapot server, and Red Tea file system
Code
<lsl>/*( Teacup Server v0.2 )*/
//-- stores the entire bootstrap or error page for touch handling string gStrAdr;
//-- pending request queue for pages waiting to be served //-- this (hopefully) prevents reponding after SL send a timeout. list gLstPndKey; list gLstPndTim;
default{
state_entry(){ //-- Request URL on start up llRequestURL(); } on_rez( integer vIntBgn ){ //-- Clear pending and request URL on rez gLstPndKey = gLstPndTim = []; llRequestURL(); } changed( integer vBitChg ){if ((CHANGED_REGION_START
Notes
Compatible File Systems and Extensions
Red Tea - A very simple backend file sysytem for a starting point].
Known Bugs
- inline script/style elements are not evaluated at load time (inline attributes should work normally)
- images have their own events, and can be used to trigger page load javascript
- document/body onload events are not triggered for .tsp pages
- images have their own events, and can be used to trigger page load javascript
Planned Upgrades
- Move the test page and base javascript library out of the main script and into either the File System, or a separate load handler script
- Add support for Site wide javascript and css or make them load normally to the page
- Trigger at least the normal load events manually
- find a convenient way to set the page title... probably use the page name
NOTE TO WIKI EDITORS
I have kept this page as a sub page of my user page to denote that it should NOT be edited except to correct errors, add confirmed bugs, or add related resources. The intent is to retain executive control in one person's hands to retain a cohesive vision of it's development. If you have an improvement suggestion, or request, please use the discussion page.