Difference between revisions of "User:Void Singer/Teacup"

From Second Life Wiki
Jump to navigation Jump to search
m (+notes)
(Major upgrade for version change)
Line 5: Line 5:


{{void-box
{{void-box
|title=Teacup
|title=Teacup/Saucer
|content=<div style="float:left;">[[File:Teacup.png|205px|thumb|left|Teacup]]</div>
|content=
=== What is it? ===
=== [[File:Teacup.png|frameless|left|256px|alt=Teacup/Saucer]]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, [[User:Kelly_Linden|Kelly Linden]], [[User:Torley_Linden|Torley Linden]], [[User:Tali_Rosca|Tali Rosca]], [[User:Vegas_Silverweb|Vegas Silverweb]], and special mentions to [[User:Kate_Linden|Kate]] and/or [[User:Edelman_Linden|Edelman]] 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.
----
Teapot is an open source webserver front end for LSL's HTTP-in + {{HoverText|MOAP|Media On A Prim}} functionality. It builds on the work of several other people including but not limited to, [[User:Kelly_Linden|Kelly Linden]], [[User:Torley_Linden|Torley Linden]], [[User:Tali_Rosca|Tali Rosca]], [[User:Vegas_Silverweb|Vegas Silverweb]], and special mentions to [[User:Kate_Linden|Kate]] and/or [[User:Edelman_Linden|Edelman]] Linden. 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? ===
=== 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? ===
Externally, when Teacup starts, it gets a URL and creates a Micro HTML page, which it bootstraps onto the prim face with a "data:" URN. Once loaded, that page requests a JavaScript library, which loads normally. Once loaded, the library bootstraps the specially prepared ".tsp" HTML content onto the current page, and captures all links targeting other ".tsp" pages, so that it can do the same when they are requested. Internally the server broadcasts a message when it gets a page request, and any installed File Service scripts can reply with content, or the server will simply report back that it doesn't have the file requested if it doesn't receive a reply in a set time.
Teacup serves a modified html page.
 
* The file name should end with ".tsp" (Teacup server page)
=== Why all the tea jokes? ===
* The contents are limited to what's valid ''inside'' a normal html "body" tag.
----
* It also requires the following minor changes, made in order:
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 [http://en.wikipedia.org/wiki/April_Fools%27_Day_RFC april fool's joke] passed around as an [http://tools.ietf.org/html/rfc2324 official sounding memo] about creating a protocol for [http://en.wikipedia.org/wiki/Hyper_Text_Coffee_Pot_Control_Protocol 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 --> {{HoverText|SIP|Server In a Prim}} --> many different types of tea --> Success!
*# 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)
... And thus was born the [[Server In a Prim|SIP]], [[User:Void_Singer/Teacup|Teacup]] server, and [[User:Void_Singer/Red_Tea|Red Tea]] File Service<br>(developers are encouraged to keep the theme, but it's not a requirement)
*# all line breaks should be converted to "\n" literals (may be applied on a line by line basis)
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
*# 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!
{{void-box
|title=Quick Start
|content=
So you don't want to read a bunch of tech babble, you just want to make a website in a prim right? Well I can sympathize so here are some (mostly) easy steps to get you up and running fast...
# Rez a box and name it something creative, like "My website"
# Follow the instructions on [[User:Void_Singer/Red_Tea|This Page]] or choose a File Service from the [[User:Void_Singer/Teacup#Compatible_File_Services_and_Extensions|Compatible File Services and Extensions]] and follow those instructions.
# Copy the text in the grey box below [[User:Void_Singer/Teacup#Saucer|Code: Saucer]] into a script named "Saucer v0.3", save it, and drop it in your rezzed box
# Copy the text in the grey box below [[User:Void_Singer/Teacup#Teacup|Code: Teacup]] into a script named "Teacup v0.3", save it, and drop it in your rezzed box
# Click on the top of your box if your webpage isn't already showing.
#* At this point you may want to rotate and/or resize your box.
#* If you like, you have my express permission to download (right click, "save as") and edit (with whatever image editor you like) the [[:File:Teacup.png|Teacup.png]] file to use as the default texture of your website prim.
# Show it off to your friends with {{HoverText|MOAP|Media On A Prim}} enabled viewers
 


... And thus was born the SIP, Teapot server, and Red Tea file system
If you run into any problems, simply start a thread with the problem you are having [http://community.secondlife.com/t5/LSL-Scripting/bd-p/LSLScripting here], and I or some other technical wizard will assist you.
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
}}
}}
Line 30: Line 44:
|title=Code
|title=Code
|content=
|content=
<lsl>/*( Teacup Server v0.2 )*/
=== Teacup ===
----
* Save in a script named "Teacup"
<lsl>/*( Teacup Server v0.3 )*/


//-- stores the entire bootstrap or error page for touch handling
//-- stores the entire bootstrap or error page for touch handling
string gStrAdr;
string gStrErr0 = "data:text/html;charset=utf-8,
<html>
    <body>
        <h1 style='align: center;'>Out of Service</h1>
        <hr/>
        <p>Url Interface Unavailable or a Server script failed to respond</p>
        <hr/>
        <p style='text-align:center;font-size:50%;'>Teacup/Saucer v0.3<br/>
          <a href='secondlife:///app/agent/";
string gStrErr1 = "/about'>Contact Owner</a></p>
    </body>
</html>";


//-- pending request queue for pages waiting to be served
//-- pending request queue for pages waiting to be served
Line 39: Line 67:
list  gLstPndKey;
list  gLstPndKey;
list  gLstPndTim;
list  gLstPndTim;
key    gKeyRqs;
integer gIntRqs;


default{
default{
Line 53: Line 84:
      
      
     changed( integer vBitChg ){
     changed( integer vBitChg ){
         if ((CHANGED_REGION_START | CHANGED_REGION) & vBitChg && 1){
         if ((CHANGED_REGION_START | CHANGED_REGION) & vBitChg){
             //-- Clear pending and request URL on region restart, or region change
             //-- Clear pending and request URL on region restart, or region change
             gLstPndKey = gLstPndTim = [];
             gLstPndKey = gLstPndTim = [];
Line 62: Line 93:
     touch_end( integer vIntTch ){
     touch_end( integer vIntTch ){
         if (!llDetectedTouchFace( 0 )){
         if (!llDetectedTouchFace( 0 )){
             //-- Allow external load for those that don't have shared media, or have it disabled.
             llMessageLinked( llGetLinkNumber(), 418, "", gKeyRqs  = llDetectedKey( 0 ));
             llLoadURL( llDetectedKey( 0 ), "Teacup Page Loader for " + llGetObjectName(), gStrAdr );
             if (!~llListFindList( gLstPndKey, [gKeyRqs = " " + (string)gKeyRqs] )){
                ++gIntRqs;
                gLstPndKey = [gKeyRqs] + gLstPndKey;
                gLstPndTim = [llGetUnixTime() + 2] + gLstPndTim;
                llSetTimerEvent( 3.0 );
            }
         }
         }
     }
     }
Line 69: Line 105:
     http_request( key vKeySrc, string vStrMth, string vStrBdy ){
     http_request( key vKeySrc, string vStrMth, string vStrBdy ){
         if (URL_REQUEST_GRANTED == vStrMth){ //-- Got a url, push the bootsrap page
         if (URL_REQUEST_GRANTED == vStrMth){ //-- Got a url, push the bootsrap page
             gStrAdr = "data:text/html;charset:utf-8,
             llSetPrimitiveParams( [PRIM_TEXT, vStrBdy + "/", ZERO_VECTOR, 0.0] );
            llMessageLinked( llGetLinkNumber(), 418, "", gKeyRqs = llGetKey() );
            ++gIntRqs;
            gLstPndKey = [gKeyRqs = " " + (string)gKeyRqs] + gLstPndKey;
            gLstPndTim = [llGetUnixTime() + 2] + gLstPndTim;
            llSetTimerEvent( 3.0 );
        }else if (URL_REQUEST_DENIED == vStrMth){ //-- didn't get a url, push a warning page
            llSetPrimitiveParams( [PRIM_TEXT, "", ZERO_VECTOR, 0.0] );
            vStrBdy = gStrErr0 + (string)llGetOwner() + gStrErr1;
            llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, vStrBdy, PRIM_MEDIA_CURRENT_URL, vStrBdy] );
        }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 ) )){
                //-- 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];
                if (gLstPndTim == [""]){
                    llSetTimerEvent( 3.0 );
                }
            }
        }
    }
   
    link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
        //-- only accept return status codes we understand.
        if (~vIntSrc = llListFindList( [101, 200, 201, 202, 203, 404], [vIntDta] )){
            if (!vIntSrc){ //-- 101 is special handling
                vKeyDta = " " + (string)(gKeyRqs = vKeyDta);
            }
            //-- is the request still waiting to be served?
            if (~vIntSrc = llListFindList( gLstPndKey, [vKeyDta] )){
                if (vKeyDta){
                    //-- server the request, remove it from the pending queue
                    llHTTPResponse( vKeyDta, vIntDta, vStrDta );
                }else{
                    if (llGetKey() == gKeyRqs){
                        llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, vStrDta, PRIM_MEDIA_CURRENT_URL, vStrDta] );
                    }else{
                        llLoadURL( gKeyRqs, "Teacup Server: " + llGetObjectName(), vStrDta );
                    }
                }
                gLstPndKey = llDeleteSubList( gLstPndKey, vIntSrc, vIntSrc );
                gLstPndTim = llDeleteSubList( gLstPndTim, vIntSrc, vIntSrc );
            }
        }
    }
   
    timer(){ //-- optimally, this runs once on an empty list for 3sec worth of server requests
        if (gLstPndTim != []){ //-- are there items in the pending queue?
            while (llList2Integer( gLstPndTim, 0 ) < llGetUnixTime()){
                if (gIntRqs){
                    if (gKeyRqs = llDeleteSubString( llList2String( gLstPndKey, 0 ), 0, 0 )){
                        if (llGetKey() == gKeyRqs){
                            llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, gStrErr0 + (string)llGetOwner() + gStrErr1,
                                                      PRIM_MEDIA_CURRENT_URL, gStrErr0 + (string)llGetOwner() + gStrErr1] );
                        }else{
                            llLoadURL( gKeyRqs, "Teacup Server: " + llGetObjectName(), gStrErr0 + (string)llGetOwner() + gStrErr1 );
                        }
                        --gIntRqs;
                    }
                } //--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( (float)(gIntRqs = 0) );
                    return;
                }
            }
        }else{
            llSetTimerEvent( 0.0 );
        }
    }
}
/*//--                          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]]
=== Saucer ===
----
* Save in a script named "Saucer"
<lsl>/*( Saucer v0.3_ )*/
 
/*//-- Server Default Page Variables --//*/
string  gStrTS0 = "data:text/html;charset:utf-8,
<html>
<html>
     <head>
     <head>
         <base href='" + vStrBdy + "/'>
         <base href='"; //-- Insertion point: Base server address (pulled from prim description)
         <script type='text/javascript' src='teacup.js' defer='1'></script>
string gStrTS1 = "/'>
        <title>"; //-- Insertion point: Title (pulled from prim name)
string gStrTS2 = "</title>
         <script type='text/javascript' defer='1' src='teacup.js'></script>"; //-- Insertion point: site js/css if present (unsupported as of yet)
string  gStrTS3 = "
     </head>
     </head>
     <body>
     <body>
         <h1>Teacup Server Test Page</h1>
         <h1>Teacup Server Test Page</h1>
         <hr/>
         <hr/>
         <p>ZOMG it works. Maybe you should add some tea leaves now?</p>
         <p>ZOMG it works. If you are Still reading this, The File Service failed to respond or there is no index page available</p>
         <hr/>
         <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>
         <p style='text-align:center;font-size:50%;'>"; //-- Insertion point: Known installed services
string  gStrTS4 = "<br/><a href='secondlife:///app/agent/"; //-- Insertion point:  Owners key for contact
string gStrTS5 = "/about'>Contact Owner</a>
     </body>
     </body>
</html>";
</html>";
            llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, gStrAdr, PRIM_MEDIA_CURRENT_URL, gStrAdr] );
list    gLstSvc = ["Teacup/Saucer v0.3"]; //-- Known Services (only partially implemented)
         }else if (URL_REQUEST_DENIED == vStrMth){ //-- didn't get a url, push a warning page
 
            gStrAdr = "data:text/html;charset=utf-8,
/*//-- Request Handler variables --//*/
<html>
list    gLstPag = [""]; //-- List of known pages
    <body>
list    gLst404Tim; //-- 404 timeout
        <h1 style='align: center;'>Unable to Generate URL interface</h1>
list    gLst404Key; //-- 404 request key
        <hr/>
list    gLst404Pag; //-- 404 page name (used for discovery only)
        <p>Blah, this tastes like coffee!</p>
integer gIntLnk;    //-- link number to save on function calls
        <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>
default{
    </body>
    state_entry(){
</html>";
        gIntLnk = llGetLinkNumber();
             llSetPrimMediaParams( 0, [PRIM_MEDIA_HOME_URL, gStrAdr, PRIM_MEDIA_CURRENT_URL, gStrAdr] );
        llMessageLinked( LINK_SET, 418, " Server Start", NULL_KEY );
         }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
    link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
             if (vStrMth = llUnescapeURL( llDeleteSubString( llGetHTTPHeader( vKeySrc, "x-path-info" ), 0, 0 ) )){
         if (vKeyDta){ //-- server traffic?
                 if ("teacup.js" == vStrMth){ //-- if it's our bootsrap js page, we can go ahead and server that straight away
            if (418 == vIntDta && gIntLnk == vIntSrc){ //-- server request?
                     llHTTPResponse( vKeySrc, 200,  "/*  teacup.js <!-- */
                if (!~vIntDta = llListFindList( gLstPag, [vStrDta = llList2String( llParseStringKeepNulls( vStrDta, ["?", "#"], [] ), 0 )] )){ //-- is that an invalid page?
                    gLst404Key += [vKeyDta];
                    gLst404Pag += [vStrDta];
                    gLst404Tim += [llGetUnixTime() + 2]; //-- this actually amount to a ~2.25sec timeout
                    if (gLst404Tim == [""]){ //-- only bump the timer if this is the only 404 pending.
                        llSetTimerEvent( 0.75 ); //-- give unadvertised backend services time to respond
                    }
                }else if (!vIntDta){
                    llMessageLinked( vIntSrc,
                                    101, //-- Server Push Root Mode //-- Next line: -- Insert Address
                                    gStrTS0 + llList2String( llGetPrimitiveParams( [PRIM_TEXT] ), 0 ) +
                                    gStrTS1 + llGetObjectName() +                //-- Insert Title
                                    gStrTS2 + //-- not yet implemented            //-- Insert site js/css
                                    gStrTS3 + llDumpList2String( gLstSvc, " " ) + //-- Insert Known Services
                                    gStrTS4 + (string)llGetOwner() +             //-- Insert Owner
                                    gStrTS5,                                      //-- End Default file
                                    vKeyDta );
                }
             }else if (~vIntSrc = llListFindList( gLst404Key, [vKeyDta] )){
                if (200 == vIntDta){ //-- auto discovery of  unadvertised pages
                    gLstPag += [llList2String( gLst404Pag, vIntSrc )];
                }
                gLst404Tim = llDeleteSubList( gLst404Tim, vIntSrc, vIntSrc );
                gLst404Key = llDeleteSubList( gLst404Key, vIntSrc, vIntSrc );
                gLst404Pag = llDeleteSubList( gLst404Pag, vIntSrc, vIntSrc );
            }
         }else if (22 == llAbs( vIntDta ) && NULL_KEY == (string)vKeyDta){
            //-- add handler for svc string and site js/css
            if (vIntDta & 0x800000){ //-- negative = remove
                if (~vIntDta = llListFindList( gLstPag, [vStrDta] )){
                    gLstPag = llDeleteSubList( gLstPag, vIntDta, vIntDta );
                }
             }else{ //-- positive == add
                if (!~vIntDta = llListFindList( gLstPag, [vStrDta] )){
                    //-- add handling for default js/css injection.
                    gLstPag += [vStrDta];
                }
             }
        }
    }
   
    timer(){
        //-- timer for 404 messages
        if (gLst404Tim != []){ //-- are there items in the pending queue?
            while (llList2Integer( gLst404Tim, 0 ) < llGetUnixTime()){
                //--send 404 then loop through and remove expired pending 404s
                llMessageLinked( llGetLinkNumber(), 404, "Not Found", llList2Key( gLst404Key, 0 ) );
                gLst404Tim = llDeleteSubList( gLst404Tim, 0, 0 );
                gLst404Key = llDeleteSubList( gLst404Key, 0, 0 );
                gLst404Pag = llDeleteSubList( gLst404Pag, 0, 0 );
                 if (gLst404Tim == []){ //-- if the queue is emptied, stop the timer and exit
                    llSetTimerEvent( 0.0 );
                     return;
                }
            }
        }else{
            llSetTimerEvent( 0.0 );
        }
    }
}
/*//--                          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]]
=== teacup.js ===
----
* [[User:Void_Singer/Red_Tea|Red Tea]] expects this file saved in a notecard named "teacup.js"
* Other File systems may have different expectations for loading plain text files; Consult their documentation for details.
<nowiki>/*  teacup.js v0.3 <!-- */
                      
                      
function addEventListener( instance, eventName, listener ){
function addEventListener( instance, eventName, listener ){
Line 120: Line 322:
         uSteep( this.href );
         uSteep( this.href );
     }else{
     }else{
        var vSugar = document.createElement( 'link' );
        vSugar.setAttribute( 'rel', 'stylesheet' );
        vSugar.setAttribute( 'type', 'text/css' );
        document.getElementsByTagName( 'head' )[ 0 ].appendChild( vSugar );
        vSugar.href = 'teacup.css';
         uSteep( 'index.tsp' );
         uSteep( 'index.tsp' );
     }
     }
Line 127: Line 334:
     vTeabag = document.createElement( 'script' );
     vTeabag = document.createElement( 'script' );
     vTeabag.setAttribute( 'type', 'text/javascript' );
     vTeabag.setAttribute( 'type', 'text/javascript' );
     document.getElementsByTagName( 'head' ).item( 0 ).appendChild( vTeabag );
     document.getElementsByTagName( 'head' )[ 0 ].appendChild( vTeabag );
     vEggTimer = setTimeout( 'uPour( \\'504 Gateway Timeout\\' )', 23000 );
     vEggTimer = setTimeout( 'uPour( \\'504 Gateway Timeout\\' )', 23000 );
     addEventListener( vTeabag, 'load', uPour );
     addEventListener( vTeabag, 'load', uPour );
Line 141: Line 348:
         alert( vErr + '\\n' + vTeabag.src.substring( 81 ) );
         alert( vErr + '\\n' + vTeabag.src.substring( 81 ) );
     }else{
     }else{
         document.getElementsByTagName( 'body' ).item( 0 ).innerHTML = vTea;
         document.getElementsByTagName( 'body' )[ 0 ].innerHTML = vTea;
         for (i = 0; i < document.links.length; i++){
         for (i = 0; i < document.links.length; i++){
             if (~document.links[ i ].href.indexOf( '.tsp' )){
             if (~document.links[ i ].href.indexOf( '.tsp' )){
Line 154: Line 361:
uBoil( '' );
uBoil( '' );


/* --> */" );
/* --> */</nowiki>
                }else{ //-- request is for content
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
                    //-- 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
{{void-box
                    gLstPndKey += [vKeySrc];
|title=Protocol
                    gLstPndTim += [llGetUnixTime() + 20];
|content=
                    llSetTimerEvent( 3.0 );
This section is not required reading for users, it is really only of use to scripters looking understand of extend it.
                }
=== Server Protocols ===
            }
----
        }
==== File Requests ====
    }
The server communicates it's needs in the following format
   
<lsl>llMessageLinked( LINK_SET, REQUEST_CODE, REQUEST_PAGE, REQUEST_KEY );</lsl>
    link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
* LINK_SET: Request is broadacst to all prims (exception, the root page "" is requested in the local prim only)
        //-- only accept return status codes we understand.
* REQUEST_CODE: at this time, for simplicity, there is only one request code, 418
        if (~llListFindList( [200, 404], [vIntDta] )){
* REQUEST_PAGE: the format is [<page name>?<search string>]
            //-- list find lets us support  more codes easily, but is inefficient for only two. Need to decide if we want to support more (and insert them)
** this will be extended for POST pages, format is undecided at this time
            //-- or reserve them for internal use and change this to a more efficient comparison like ((200 == vIntDta) | (404 == vIntDta))
* REQUEST_KEY: the return key for the requested data.
            //-- is the request still waiting to be served?
** NULL_KEY and a page name of " Server Start" will be sent when the server file availability handler resets.
            if (~vIntSrc = llListFindList( gLstPndKey, [vKeyDta] )){
 
                //-- server the request, remove it from the pending queue
==== Server Notes ====
                llHTTPResponse( vKeyDta, vIntDta, vStrDta );
* The server takes the site title from the Prim name it's in
                gLstPndKey = llDeleteSubList( gLstPndKey, vIntSrc, vIntSrc );
* The hovertext of the prim the server is in will contain it's base address (invisibly)
                //-- reset the timer for the queue based on whether the queue is empty or not
* The Server is '''2''' scripts (Teacup & Saucer) and 2 default files (teacup.js & teacup.css)
                llSetTimerEvent( !!([] != gLstPndTim = llDeleteSubList( gLstPndTim, vIntSrc, vIntSrc )) * 3.0 );
** The default files require a File Service (Like [[Red Tea]]) to load
            }
* The Server includes a quick 404 handler to prevent missing or unknown files from slowing down page loading. It accepts some special commands listed below in the "File Service Protocol: Advanced" section. Any unknown page received with a 200 code for a pending request will automatically be added to the known list.
        }
^ [[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
    }
 
   
=== File Service Protocol ===
    timer(){
----
        if (gLstPndTim != []){ //-- are there items in the pending queue?
==== Basics ====
            while (llList2Integer( gLstPndTim, 0 ) < llGetUnixTime()){
File Services receive data via link messages, in the following manner
                //--loop through and remove expired pending requests
<lsl>link_message( integer REQUEST_SOURCE, integer REQUEST_CODE, string REQUEST_PAGE, key REQUEST_KEY )</lsl>
                gLstPndTim = llDeleteSubList( gLstPndTim, 0, 0 );
File Services should minimally check that REQUEST_CODE is 418 (file request), and that REQUEST_KEY is a valid key to confirm it's a valid server request.
                gLstPndKey = llDeleteSubList( gLstPndKey, 0, 0 );
: REQUEST_PAGE can be parsed with <lsl>list vLstPage = llParseStringKeepNulls( REQUEST_PAGE, ["?"], [] ); //-- The page name will be contained in: llList2String( vLstPage, 0 )</lsl>
                if (gLstPndTim == []){ //-- if the queue is emptied, stop the timer and exit
If the page name is ''NOT'' a page that this File Service handles, ''it should do nothing''. Otherwise it should return it's data in the following format:
                    llSetTimerEvent( 0.0 );
<lsl>llMessageLinked( REQUEST_SOURCE, RESPONSE_CODE, FILE_TEXT, REQUEST_KEY );</lsl>
                    return;
* REQUEST_SOURCE: the link number that the server request came from.
                }
* RESPONSE_CODE: one of the following integer values (they are all normal HTTP RESPONSE CODES)
            }
** 200 (Success: default value, can be used for anything that returns content)
        }
** 201 (Created: optional alternative, probably best if the file was created for the request)
    }
** 202 (Accepted: optional alternative, probably best for commands that generate in world actions)
}
** 204 (No Content: optional alternative, probably best for form data, FILE_TEXT should be blank)
/*//--                           License Text                          --//*/
* FILE_TEXT: text of the file that was requested.
/*// Free to copy, use, modify, distribute, or sell, with attribution.  //*/
* REQUEST_KEY: server request key for this file.
/*//    (C)2011 (CC-BY) [ http://creativecommons.org/licenses/by/3.0 ]   //*/
: Any return should be done within 20 seconds or it's results will be ignored.
/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
 
/*//  All usages must contain a plain text copy of the previous 2 lines.  //*/
==== Advanced ====
/*//--                                                                 --//*/</lsl>
There are two style in which a File System can behave. Passive, and Active. Active is preferred.
 
Active File System services should advertise each file as soon as it's available using
: ''File Added'': Sent when file is available in the file system<lsl>llMessageLinked( LINK_SET, 22, "name of the file", NULL_KEY ); //-- key MUST be the constant NULL_KEY</lsl>
When a file is removed from the file system it should send
: ''File Removed'':Sent when a file is removed from the file system<lsl>llMessageLinked( LINK_SET, -22, "name of the file", NULL_KEY ); //-- key MUST be the constant NULL_KEY</lsl>
Additionally, if and Active File System receives the " Server Start" message (418 code, plus " Server Start" text, and NULL_KEY), it should resend "File Added" for each of the files still available, but no faster than 10/second for file system.
: Alternatively, if the file system can respond to normal requests in under 2 seconds, any file that is sent with response code 200 will automatically be added, and those would not need to be sent.
 
 
Passive File Systems do not need to advertise their available files, but if they receive a request that they might reasonably expect to take more than 2 seconds to fill, they should immediately send
: '''File Exists (Continue)''': Sent to prevent a 404 message for a file from slow passive file system elements. <lsl>llMessageLinked( REQUEST_SOURCE, 100, "", REQUEST_KEY );</lsl>
and then follow up with their normal response. the 20 second return limit is still in effect.
: Passive systems should ''NOT'' use response code 200, or if they do, ''MUST'' send "File Removed" messages when the file is removed. Use codes 201, 202, and 204 instead.
^ [[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
 
=== Teacup Server Page (".tsp") Format ===
----
Teacup serves a modified html page called a Teacup Server Page (.tsp), to overcome limitations in the LSL HTTP-in behavior.
: if a requested filename ends with ".tsp" it needs to conform to a specific format that is similar but not identical to the contents of a normal html webpage. To convert an html page into a tsp page, the follow actions must take place (an example of all of them may be found in the [[Red Tea]] file system.
* 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)
*#* eg:<div style="font-family:monospace;font-size:12px;background-color:#EEE;">&nbsp;file_string <span style="color: rgb(102, 204, 102);">=</span> [[llDumpList2String|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llDumpList2String</span>]]<span style="color: rgb(102, 204, 102);">(</span> [[llParseStringKeepNulls|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llParseStringKeepNulls</span>]]<span style="color: rgb(102, 204, 102);">(</span> file_string, <span style="color: rgb(102, 204, 102);">[<span style="color: rgb(0, 160, 0);">"\\"</span>]</span>, <span style="color: rgb(102, 204, 102);">[] )</span>, <span style="color: rgb(0, 160, 0);">"\\\\"</span> <span style="color: rgb(102, 204, 102);">)</span>;</div>
*# all single quote characters must be prefixed with a backslash (may be applied on a line by line basis)
*#* eg:<div style="font-family:monospace;font-size:12px;background-color:#EEE;">&nbsp;file_string <span style="color: rgb(102, 204, 102);">=</span> [[llDumpList2String|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llDumpList2String</span>]]<span style="color: rgb(102, 204, 102);">(</span> [[llParseStringKeepNulls|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llParseStringKeepNulls</span>]]<span style="color: rgb(102, 204, 102);">(</span> file_string, <span style="color: rgb(102, 204, 102);">[<span style="color: rgb(0, 160, 0);">"'"</span>]</span>, <span style="color: rgb(102, 204, 102);">[] )</span>, <span style="color: rgb(0, 160, 0);">"\\'"</span> <span style="color: rgb(102, 204, 102);">)</span>;</div>
*# all line breaks should be converted to "\n" literals (may be applied on a line by line basis)
*#* eg:<div style="font-family:monospace;font-size:12px;background-color:#EEE;">&nbsp;file_string <span style="color: rgb(102, 204, 102);">=</span> [[llDumpList2String|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llDumpList2String</span>]]<span style="color: rgb(102, 204, 102);">(</span> [[llParseStringKeepNulls|<span style="color: rgb(160, 0, 0); font-weight: bold; text-decoration: none;">llParseStringKeepNulls</span>]]<span style="color: rgb(102, 204, 102);">(</span> file_string, <span style="color: rgb(102, 204, 102);">[<span style="color: rgb(0, 160, 0);">"\n"</span>]</span>, <span style="color: rgb(102, 204, 102);">[] )</span>, <span style="color: rgb(0, 160, 0);">"\\n"</span> <span style="color: rgb(102, 204, 102);">)</span>;</div>
*# The page should be prefixed with "vTea='" and suffixed with "';" (applies only to the whole page)
*#* eg:<div style="font-family:monospace;font-size:12px;background-color:#EEE;">&nbsp;file_string <span style="color: rgb(102, 204, 102);">=</span> <span style="color: rgb(0, 160, 0);">"vTea='"</span> <span style="color: rgb(102, 204, 102);">+</span> file_string <span style="color: rgb(102, 204, 102);">+</span> <span style="color: rgb(0, 160, 0);">"';"</span>;</div>
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
}}
}}
Line 210: Line 447:
|content=
|content=
=== Compatible File Systems and Extensions ===
=== Compatible File Systems and Extensions ===
[[User:Void_Singer/Red_Tea|Red Tea]] - A very simple backend file sytem for a starting point.
----
[[User:Void_Singer/Red_Tea|Red Tea]] - A very simple File Service System for a starting point.
 
=== Known Bugs ===
=== Known Bugs ===
----
* inline script/style elements are not evaluated at load time (inline attributes should work normally)
* 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
** images have their own events, and can be used to trigger page load javascript
* document/body onload events are not triggered for .tsp pages
* document/body onload events are not triggered for .tsp pages
** images have their own events, and can be used to trigger page load javascript
** images have their own events, and can be used to trigger page load javascript
=== Planned Upgrades ===
* Fails to load on external browsers (jira pending)
* 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
** This is a viewer problem right now, some of the internal browsers will load it
** '''Confirmed''': will be adding a "Saucer" script to handle the base .js file, and default message pages.
*** Phoenix will not load it in the internal browser, because they limit the webkit address field to 255 characters, I'm going to see if I can't work around this with cramped formatting, or get the phoenix devs to work on it
* Add support for Site wide javascript and css or make them load normally to the page
*** v2 viewers should load the index page fine to the internal browser. if you copy the address and paste it into an external browser it will work from there
** '''Confirmed''': Saucer sub script will handle page availabilities from file system, and inject site js and css into the default message page.
**** confirmed on Firefox 3.6, unknown on any others at this time.
*** May eventually lead to a new method of handling where each page can have a .js and .css page associated with it.
 
=== Requested Feature Upgrades ===
----
* Add handling for POST action
** ''Accepted'': would like feedback on how to handle this internally though.
* Add support per page javascript and css
* Trigger at least the normal load events manually
* Trigger at least the normal load events manually
* find a convenient way to set the page title... probably use the page name
* Find a simple way to serve base64 encoded content such as images or sounds.
** '''Confirmed''': Page titles will match the internal file name minus the ".tsp" extension
** '''NEED ASSISTANCE''': I can (probably) do it the same way we load pages, but this makes it hard for page writers.
*** index page may eventually assume the object name, if I can cleanly inject that into the base .js file.
* Find a convenient way to set title on per page basis... probably use the page name
** ''Accepted'': Page titles should match the internal file name minus the ".tsp" extension
 
=== Change Log / Old Versions ===
----
Most Recent Changes are at the top, old version links below.
* [[User:Void_Singer/Teacup|Teacup/Saucer v0.3]]
** Server Test Page moved to saucer file availability handler
** Server javascript page loader library (Teacup.js) moved to File Service System
*** Users should have less trouble editing the file now and can include their own functions in the same file
** Sitewide CSS supported via Teacup.css
** Saucer Script now handles page availability for quick 404 returns to speed page loading.
** Site Title is pulled from the object name
** Site address is discoverable by object scripts in the (hidden) prim hovertext
** Server now runs from any prim, not just the root.
* [[User:Void_Singer/Teacup&oldid=1140984|Teacup v0.2]]
** Initial Public Release
 
=== NOTE TO WIKI EDITORS ===
=== 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.<br>
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.<br>
The intent is to ensure executive control is in one person's hands to retain a cohesive vision of it's development.<br>
The intent is to ensure executive control is in one person's hands to retain a cohesive vision of it's development.<br>
If you have an improvement suggestion, or request, please use the [[User_Talk:Void Singer/Teacup|discussion]] page.
If you have an improvement suggestion, or request, please use the [[User_talk:Void_Singer/Teacup&action=edit&section=new|discussion]] page.
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
}}
}}
Line 236: Line 499:
|title=Questions or Comments?
|title=Questions or Comments?
|content=
|content=
Feel free to leave me a note on the [[User_Talk:Void Singer/Teacup|discussion]] page.
Feel free to leave me a note on the [[User_talk:Void_Singer/Teacup&action=edit&section=new|discussion]] page.
}}
}}

Revision as of 08:23, 19 April 2011

Teacup/Saucer

Teacup/Saucer
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 Kate and/or Edelman Linden. 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?


Externally, when Teacup starts, it gets a URL and creates a Micro HTML page, which it bootstraps onto the prim face with a "data:" URN. Once loaded, that page requests a JavaScript library, which loads normally. Once loaded, the library bootstraps the specially prepared ".tsp" HTML content onto the current page, and captures all links targeting other ".tsp" pages, so that it can do the same when they are requested. Internally the server broadcasts a message when it gets a page request, and any installed File Service scripts can reply with content, or the server will simply report back that it doesn't have the file requested if it doesn't receive a reply in a set time.

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, Teacup server, and Red Tea File Service
(developers are encouraged to keep the theme, but it's not a requirement)

Return to top

Quick Start

So you don't want to read a bunch of tech babble, you just want to make a website in a prim right? Well I can sympathize so here are some (mostly) easy steps to get you up and running fast...

  1. Rez a box and name it something creative, like "My website"
  2. Follow the instructions on This Page or choose a File Service from the Compatible File Services and Extensions and follow those instructions.
  3. Copy the text in the grey box below Code: Saucer into a script named "Saucer v0.3", save it, and drop it in your rezzed box
  4. Copy the text in the grey box below Code: Teacup into a script named "Teacup v0.3", save it, and drop it in your rezzed box
  5. Click on the top of your box if your webpage isn't already showing.
    • At this point you may want to rotate and/or resize your box.
    • If you like, you have my express permission to download (right click, "save as") and edit (with whatever image editor you like) the Teacup.png file to use as the default texture of your website prim.
  6. Show it off to your friends with MOAP enabled viewers


If you run into any problems, simply start a thread with the problem you are having here, and I or some other technical wizard will assist you.

Return to top

Code

Teacup


  • Save in a script named "Teacup"

<lsl>/*( Teacup Server v0.3 )*/

//-- stores the entire bootstrap or error page for touch handling string gStrErr0 = "data:text/html;charset=utf-8, <html>

   <body>

Out of Service


Url Interface Unavailable or a Server script failed to respond


Teacup/Saucer v0.3
<a href='secondlife:///app/agent/"; string gStrErr1 = "/about'>Contact Owner</a>

   </body>

</html>";

//-- pending request queue for pages waiting to be served //-- this (hopefully) prevents reponding after SL send a timeout. list gLstPndKey; list gLstPndTim;

key gKeyRqs; integer gIntRqs;

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

Protocol

This section is not required reading for users, it is really only of use to scripters looking understand of extend it.

Server Protocols


File Requests

The server communicates it's needs in the following format <lsl>llMessageLinked( LINK_SET, REQUEST_CODE, REQUEST_PAGE, REQUEST_KEY );</lsl>

  • LINK_SET: Request is broadacst to all prims (exception, the root page "" is requested in the local prim only)
  • REQUEST_CODE: at this time, for simplicity, there is only one request code, 418
  • REQUEST_PAGE: the format is [<page name>?<search string>]
    • this will be extended for POST pages, format is undecided at this time
  • REQUEST_KEY: the return key for the requested data.
    • NULL_KEY and a page name of " Server Start" will be sent when the server file availability handler resets.

Server Notes

  • The server takes the site title from the Prim name it's in
  • The hovertext of the prim the server is in will contain it's base address (invisibly)
  • The Server is 2 scripts (Teacup & Saucer) and 2 default files (teacup.js & teacup.css)
    • The default files require a File Service (Like Red Tea) to load
  • The Server includes a quick 404 handler to prevent missing or unknown files from slowing down page loading. It accepts some special commands listed below in the "File Service Protocol: Advanced" section. Any unknown page received with a 200 code for a pending request will automatically be added to the known list.

^ Return to top

File Service Protocol


Basics

File Services receive data via link messages, in the following manner <lsl>link_message( integer REQUEST_SOURCE, integer REQUEST_CODE, string REQUEST_PAGE, key REQUEST_KEY )</lsl> File Services should minimally check that REQUEST_CODE is 418 (file request), and that REQUEST_KEY is a valid key to confirm it's a valid server request.

REQUEST_PAGE can be parsed with <lsl>list vLstPage = llParseStringKeepNulls( REQUEST_PAGE, ["?"], [] ); //-- The page name will be contained in: llList2String( vLstPage, 0 )</lsl>

If the page name is NOT a page that this File Service handles, it should do nothing. Otherwise it should return it's data in the following format: <lsl>llMessageLinked( REQUEST_SOURCE, RESPONSE_CODE, FILE_TEXT, REQUEST_KEY );</lsl>

  • REQUEST_SOURCE: the link number that the server request came from.
  • RESPONSE_CODE: one of the following integer values (they are all normal HTTP RESPONSE CODES)
    • 200 (Success: default value, can be used for anything that returns content)
    • 201 (Created: optional alternative, probably best if the file was created for the request)
    • 202 (Accepted: optional alternative, probably best for commands that generate in world actions)
    • 204 (No Content: optional alternative, probably best for form data, FILE_TEXT should be blank)
  • FILE_TEXT: text of the file that was requested.
  • REQUEST_KEY: server request key for this file.
Any return should be done within 20 seconds or it's results will be ignored.

Advanced

There are two style in which a File System can behave. Passive, and Active. Active is preferred.

Active File System services should advertise each file as soon as it's available using

File Added: Sent when file is available in the file system<lsl>llMessageLinked( LINK_SET, 22, "name of the file", NULL_KEY ); //-- key MUST be the constant NULL_KEY</lsl>

When a file is removed from the file system it should send

File Removed:Sent when a file is removed from the file system<lsl>llMessageLinked( LINK_SET, -22, "name of the file", NULL_KEY ); //-- key MUST be the constant NULL_KEY</lsl>

Additionally, if and Active File System receives the " Server Start" message (418 code, plus " Server Start" text, and NULL_KEY), it should resend "File Added" for each of the files still available, but no faster than 10/second for file system.

Alternatively, if the file system can respond to normal requests in under 2 seconds, any file that is sent with response code 200 will automatically be added, and those would not need to be sent.


Passive File Systems do not need to advertise their available files, but if they receive a request that they might reasonably expect to take more than 2 seconds to fill, they should immediately send

File Exists (Continue): Sent to prevent a 404 message for a file from slow passive file system elements. <lsl>llMessageLinked( REQUEST_SOURCE, 100, "", REQUEST_KEY );</lsl>

and then follow up with their normal response. the 20 second return limit is still in effect.

Passive systems should NOT use response code 200, or if they do, MUST send "File Removed" messages when the file is removed. Use codes 201, 202, and 204 instead.

^ Return to top

Teacup Server Page (".tsp") Format


Teacup serves a modified html page called a Teacup Server Page (.tsp), to overcome limitations in the LSL HTTP-in behavior.

if a requested filename ends with ".tsp" it needs to conform to a specific format that is similar but not identical to the contents of a normal html webpage. To convert an html page into a tsp page, the follow actions must take place (an example of all of them may be found in the Red Tea file system.
  • 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:
    1. all backslash characters must be converted to double backslashes (may be applied on a line by line basis)
    2. all single quote characters must be prefixed with a backslash (may be applied on a line by line basis)
    3. all line breaks should be converted to "\n" literals (may be applied on a line by line basis)
    4. The page should be prefixed with "vTea='" and suffixed with "';" (applies only to the whole page)
      • eg:
         file_string = "vTea='" + file_string + "';";
Return to top

Notes

Compatible File Systems and Extensions


Red Tea - A very simple File Service System 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
  • Fails to load on external browsers (jira pending)
    • This is a viewer problem right now, some of the internal browsers will load it
      • Phoenix will not load it in the internal browser, because they limit the webkit address field to 255 characters, I'm going to see if I can't work around this with cramped formatting, or get the phoenix devs to work on it
      • v2 viewers should load the index page fine to the internal browser. if you copy the address and paste it into an external browser it will work from there
        • confirmed on Firefox 3.6, unknown on any others at this time.

Requested Feature Upgrades


  • Add handling for POST action
    • Accepted: would like feedback on how to handle this internally though.
  • Add support per page javascript and css
  • Trigger at least the normal load events manually
  • Find a simple way to serve base64 encoded content such as images or sounds.
    • NEED ASSISTANCE: I can (probably) do it the same way we load pages, but this makes it hard for page writers.
  • Find a convenient way to set title on per page basis... probably use the page name
    • Accepted: Page titles should match the internal file name minus the ".tsp" extension

Change Log / Old Versions


Most Recent Changes are at the top, old version links below.

  • Teacup/Saucer v0.3
    • Server Test Page moved to saucer file availability handler
    • Server javascript page loader library (Teacup.js) moved to File Service System
      • Users should have less trouble editing the file now and can include their own functions in the same file
    • Sitewide CSS supported via Teacup.css
    • Saucer Script now handles page availability for quick 404 returns to speed page loading.
    • Site Title is pulled from the object name
    • Site address is discoverable by object scripts in the (hidden) prim hovertext
    • Server now runs from any prim, not just the root.
  • Teacup v0.2
    • Initial Public Release

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 ensure executive control is 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.

Return to top

Questions or Comments?

Feel free to leave me a note on the discussion page.