Monday, November 08, 2010 at 12:06 AM.
system.verbs.builtins.webserver.responders.default.methods.GET
<<Script: system.verbs.builtins.webserver.responders.default.methods.GET; Version 2; Date: Thu, 21 May 1998 17:45:30 GMT; ID: RAB
<<Add the checking of form so that tables or directories taht are missing the trailing / gets redirected to include the /
<<Script: system.verbs.builtins.webserver.responders.default.methods.GET; Version 1; Date: Fri, 08 May 1998 17:44:37 GMT; ID: RAB
<<General information
<<This responder will serve data out of the ODB or the file system. If a script is in the server path, the result of that script is what gets served.
<<All elements to be served live in the default.data.docTree table. This table may be nested if desired and 3 types exhibit "special" behavior.
<<If an element in the objects table is of type "address", that value will be dereferenced and URI traversal will continue from that point.
<<If an element in the objects table is of type "filespec", that value will be used and serving will commence from the file system.
<<If an element in the objects table is of type "script", then that script will be executed and the result returned.
<<Options/User settings
<<default.data.docTree is the root in which all items are served. The Web Admin can add anthing they would like published to this table .
<<default.data.prefs contains the options that control what is displayed
<<default.data.prefs.fileAutoIndex: When set true will generate an HTML page of a file directory if so requested.
<<Note: You will never get a direcotry listing if a default file is present in the directory.
<<default.data.prefsfileDefault: Is a list of file names in desired order. If the URI specifies a directory and starting from the
<<beginning of that list one of those files is found in the specified directory, it will be served as if that filename was
<<part of the URI.
<<default.data.prefs.ODBautoIndex: When set true will generate an HTML page of a table if so reqested.
<<Note: You will never get a table listing if a default file is present in the table
<<default.data.prefs.ODBdefault: Is a list of ODB names in desired order. If the URI specifies a table and starting from the
<<beginning of that list one of those elements is found in the specified table, it will be served as if that element was
<<part of the URI.
on get (adrParamTable) {
local (objType, mimeType); //For type info
local (adrRequestHeaders, adrResponseHeaders);
local (dataTableAdr, responderTableAdr, docTreeTableAdr, prefsTableAdr);
local (objName);
on getFileTypeInfo(name) {
if file.isFolder (name) {
mimeType = "text/directory";
objType = ' '}
else {
objType = file.type (name);
case sys.os() { // decide what the MIME type is
osMacOS {
if defined (user.webserver.prefs.type2MIME.[objType]) {
mimeType = user.webserver.prefs.type2MIME.[objType]}
else {
mimeType = "text/html"}};
osWin95;
osWinNT {
local (ext = objType); // this might not always work
ext = string.replaceAll (ext, " ", ""); // strip spaces
if defined (user.webserver.prefs.ext2MIME.[ext]) {
mimeType= user.webserver.prefs.ext2MIME.[ext]}
else {
mimeType = "text/plain"}}}}};
on getODBTypeInfo(name) {
if (typeOf (name^) == binaryType) {
objType = getBinaryType (name^)}
else {
objType = typeOf (name^)};
if defined (user.webserver.prefs.type2MIME.[objType]) {
mimeType = user.webserver.prefs.type2MIME.[objType]}
else {
mimeType = "text/html"}};
on addFileItem (name, elementName) {
local (hrefName, elementModTime, elementSize);
elementName = string.popTrailing (elementName, file.getPathChar());
hrefName = adrParamTable^.URI;
if not (hrefName endsWith "/") {
hrefName = hrefName + "/"};
hrefName = hrefName + string.urlEncode (elementName);
elementName = xml.convertToDisplayName (elementName);
elementModTime = file.modified(name);
if file.isFolder (name) {
elementSize = "-";
if not (hrefName endsWith "/") {
hrefName = hrefName + "/"}}
else {
elementSize = string.megabyteString (file.size(name))};
getFileTypeInfo(name);
return (webserver.util.directoryDisplay.add (mimeType, hrefName, elementName, elementSize, elementModTime))};
on addODBItem (name) {
local (elementName, hrefName, elementModTime, elementSize);
elementName = nameOf(name^);
hrefName = adrParamTable^.URI;
if not (hrefName endsWith "/") {
hrefName = hrefName + "/"};
hrefName = hrefName + string.urlEncode (elementName);
elementName = xml.convertToDisplayName (elementName);
elementSize = sizeOf (name^);
if objType == outlineType {
elementSize = elementSize + " lines"}
else {
if objType == tableType {
elementSize = elementSize + " items";
if not (hrefName endsWith "/") {
hrefName = hrefName + "/"}}
else {
if elementSize > 9999 { //if greater then 4 characters then convert to Mb or Kb
elementSize = string.megabyteString (elementSize)}}};
elementModTime = timeModified(name);
return (webserver.util.directoryDisplay.add (mimeType, hrefName, elementName, elementSize, elementModTime))};
on checkForm() {
<<Check for correct form (i.e. trailing backslash) or redirect (called if a table or directory)
if not (adrParamTable^.path endsWith "/") {
if sizeof(adrParamTable^.path) > 0 {
adrResponseHeaders^.location = adrParamTable^.URI + "/";
adrResponseHeaders^.URI = adrResponseHeaders^.location; // I think this is to support OLD style redirects
adrParamTable^.code = 301; //Yes 301 not 302 since this is a permanent redirect
adrParamTable^.responseBody = webserver.util.buildErrorPage ("301 Moved Permanently", "Your specification of " + adrParamTable^.path + " was missing the trailing /");
return (false)}};
return (true)};
adrRequestHeaders = @adrParamTable^.requestHeaders;
adrResponseHeaders = @adrParamTable^.responseHeaders;
responderTableAdr = adrParamTable^.responderTableAdr; // the responder table
dataTableAdr = @responderTableAdr^.["data"]; //This table contains the user data for this responder
docTreeTableAdr = @dataTableAdr^.["docTree"]; //This table is the base "server" root
prefsTableAdr = @dataTableAdr^.["prefs"]; //This table contains the user prefs for this responder
bundle { //parse the URl into an ODB/file path
objName = string.urlDecode(adrParamTable^.path);
if objName beginswith "/odb" {
objName = string.replace (objName, "/odb", "")};
if objName beginswith "/file" {
objName = string.replace (objName, "/file", "")};
<<The following is the main security and path processing script. The result from this script is an error or the final
<<file or odb specification for the responder to serve.
if ! webserver.util.pathToAddressExt (objName, docTreeTableAdr, adrParamTable) {
return (true)}; //The error is already set.
objName = adrParamTable^.resultPath}; //as returned from pathToAddressExt
if adrParamTable^.resultType == addressType { // we are serving out of the ODB
if (defined (adrHeaderTable^.["If-Modified-Since"]) && (date (adrHeaderTable^.["If-Modified-Since"]) >= timeModified (address(objname)))) {
adrParamTable^.code = 304; // the cached object is fresh, so don't send it again
return (true)};
on doscript(name) {
<<do it
adrResponseHeaders^.["Content-Type"] = "text/html";
adrParamTable^.code = 200;
adrParamTable^.responseBody = callscript (name, nil, adrParamTable);
return (true)};
on serve (name) {
getODBTypeInfo(name);
if (objType == scriptType) or (objType == codeType) {
return (doScript(name))};
adrParamTable^.responseBody = string(name^);
adrResponseHeaders^.["Content-Type"] = mimeType;
adrParamTable^.code = 200;
return (true)};
<<Start the serving process
getODBTypeInfo(objName);
if objType == tableType {
local (i);
<<Check for correct form (i.e. trailing backslash) or redirect
if not checkForm() {
return (true)};
<<Check for the default element to render
for i in prefsTableAdr^.ODBdefault {
local (default);
default = @objName^.[i];
if defined (default^) {
adrParamTable^.path = string.popTrailing (adrParamTable^.path, "/");
adrParamTable^.path = adrParamTable^.path + "/" + i;
adrParamTable^.URI = string.urlEncode(adrParamTable^.path);
return (this^ (adrParamTable))}}; // Yes we recurse here so default behaves correctly
if prefsTableAdr^.ODBautoIndex {
local (responseText, leafName);
adrResponseHeaders^.["Last-Modified"] = date.netstandardString ( timeModified (objName));
adrResponseHeaders^.["Content-Type"] = "text/html";
responseText = webserver.util.directoryDisplay.start (adrParamTable^.path);
for i = 1 to sizeof(objName^) {
leafName = @objName^[i];
getODBTypeInfo(leafName);
if objType == filespecType {
if file.exists (leafName^) {
responseText = responseText + addFileItem (leafName^, nameof (leafName^))}}
else {
responseText = responseText + addODBItem (leafName)}};
adrParamTable^.responseBody = responseText + webserver.util.directoryDisplay.finish()}
else { //Tables are not served
adrParamTable^.code = 403;
adrParamTable^.responseBody = webserver.util.buildErrorPage ("403 Forbidden", "Access to the object " + adrParamTable^.path + " is forbidden.");
return (true)}}
else { // Not a table
return (serve (objName))};
adrParamTable^.code = 200}
else { //otherwise it should be serving out of the file system
if adrParamTable^.resultType == filespecType { // we are serving out of the File System
if (defined (adrHeaderTable^.["If-Modified-Since"]) && (date (adrHeaderTable^.["If-Modified-Since"]) >=file.modified(objname))) {
adrParamTable^.code = 304; // the cached object is fresh, so don't send it again
return (true)};
on serve (name) {
getFileTypeInfo (name);
adrResponseHeaders^.["Last-Modified"] = date.netstandardString ( file.modified (name));
adrResponseHeaders^.["Content-Type"] = mimeType;
if defined (adrRequestHeaders^.["Accept-Encoding"]) { // cool encoding magic
if ((adrRequestHeaders^.["Accept-Encoding"] contains "deflate") && (file.exists (name+".def"))) {
<<the client accepts deflated files and we just happen to have one
if (file.modified (name+".def") >= file.modified (name)) { // make sure it's not an old version
adrResponseHeaders^.["Content-Encoding"] = "deflate";
adrResponseHeaders^.["Vary"] = "Accept-Encoding"; // let caches know that we negotiated this response
name = name + ".def"}};
if ((adrRequestHeaders^.["Accept-Encoding"] contains "gzip") && (file.exists (name +".gz"))) {
<<the client accepts gzipped files and we just happen to have one
if (file.modified (name+".gz") >= file.modified (name)) { // make sure it's not an old version
adrResponseHeaders^.["Content-Encoding"] = "gzip";
adrResponseHeaders^.["Vary"] = "Accept-Encoding"; // let caches know that we negotiated this response
name = name + ".gz"}}};
adrParamTable^.code = 200;
adrParamTable^.responseBody = file.readWholeFile (name);
return (true)};
<<Start the serving process
if file.isFolder (objName) {
local (i);
<<Check for correct form (i.e. trailing backslash) or redirect
if not checkForm() {
return (true)};
<<Check for the default element to render
for i in prefsTableAdr^.fileDefault {
local (default);
default = string.popTrailing (objName, file.getPathChar()) + file.getPathChar() + i;
if file.exists (default) {
return(serve(default))}};
<<There is no default file - serve the directory if desired
if prefsTableAdr^.fileAutoIndex {
local (responseText, leafName);
adrResponseHeaders^.["Last-Modified"] = date.netstandardString ( file.modified (objName));
adrResponseHeaders^.["Content-Type"] = "text/html";
responseText = webserver.util.directoryDisplay.start (adrParamTable^.path);
fileloop (leafName in objName) {
if file.isVisible (leafName) {
responseText = responseText + addFileItem (leafName, file.fileFromPath (leafName))};
};
adrParamTable^.responseBody = responseText + webserver.util.directoryDisplay.finish()}
else { //Directories are not served
adrParamTable^.code = 403;
adrParamTable^.responseBody = webserver.util.buildErrorPage ("403 Forbidden", "Access to the object " + adrParamTable^.path + " is forbidden.");
return (true)}}
else { // Just a simple file...
return (serve (objName))};
adrParamTable^.code = 200}
else { //this should never occur
adrParamTable^.code = 500;
adrParamTable^.responseBody = webserver.util.buildErrorPage ("500 Internal Server Error", "The type of the object " + adrParamTable^.path + " is unknown.")}};
return (true)}
<<bundle //Testing code
<<local (paramTable)
<<new (tableType, @paramTable)
<<new (tableType, @paramTable.requestHeaders)
<<new (tableType, @paramTable.responseHeaders)
<<paramTable.responder = "default"
<<paramTable.responderTableAdr = @user.webserver.responders.default
<<paramTable.path = "/misc/foo"
<<paramTable.URI = string.urlEncode (paramTable.path)
<<get (@paramTable)
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.