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.