From 3129d5bf526a867beb9cd3ca1de8dd70600557c6 Mon Sep 17 00:00:00 2001 From: "jwalden%mit.edu" Date: Fri, 26 Jan 2007 23:48:34 +0000 Subject: [PATCH] Bug 368211 - Pretty generated directory listings for directories not containing index.html in the JS HTTP server. r=sayrer git-svn-id: svn://10.0.0.236/trunk@219019 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/netwerk/test/httpserver/httpd.js | 81 +++++- .../test/test_default_index_handler.js | 268 ++++++++++++++++++ .../httpserver/test/test_setindexhandler.js | 13 +- 3 files changed, 353 insertions(+), 9 deletions(-) create mode 100644 mozilla/netwerk/test/httpserver/test/test_default_index_handler.js diff --git a/mozilla/netwerk/test/httpserver/httpd.js b/mozilla/netwerk/test/httpserver/httpd.js index d7cdb59113b..21728d52daf 100644 --- a/mozilla/netwerk/test/httpserver/httpd.js +++ b/mozilla/netwerk/test/httpserver/httpd.js @@ -61,7 +61,7 @@ function NS_ASSERT(cond, msg) if (debugEnabled() && !cond) { dumpn("###!!!"); - dumpn("###!!! ASSERTION: " + msg); + dumpn("###!!! ASSERTION" + (msg ? ": " + msg : "!")); dumpn("###!!! Stack follows:"); var stack = new Error().stack.split(/\n/); @@ -549,17 +549,88 @@ function createHandlerFunc(handler) return function(metadata, response) { handler.handle(metadata, response); }; } + /** - * The default handler for directories. + * The default handler for directories; writes an HTML response containing a + * slightly-formatted directory listing. */ function defaultIndexHandler(metadata, response) { - var file = metadata.getProperty("directory"); - NS_ASSERT(file); + response.setHeader("Content-Type", "text/html", false); - throw HTTP_501; // need directory listings ftw! + var path = htmlEscape(metadata.path); + + // + // Just do a very basic bit of directory listings -- no need for too much + // fanciness, especially since we don't have a style sheet in which we can + // stick rules (don't want to pollute the default path-space). + // + + var body = '\ + \ + ' + path + '\ + \ + \ +

' + path + '

\ +
    '; + + var directory = metadata.getProperty("directory"); + NS_ASSERT(directory && directory.isDirectory()); + + var fileList = []; + var files = directory.directoryEntries; + while (files.hasMoreElements()) + { + var f = files.getNext().QueryInterface(Ci.nsIFile); + if (!f.isHidden()) + fileList.push(f); + } + + fileList.sort(fileSort); + + for (var i = 0; i < fileList.length; i++) + { + var file = fileList[i]; + try + { + var name = file.leafName; + var sep = file.isDirectory() ? "/" : ""; + + // Note: using " to delimit the attribute here because encodeURIComponent + // passes through '. + var item = '
  1. ' + + htmlEscape(name) + sep + + '
  2. '; + + body += item; + } + catch (e) { /* some file system error, ignore the file */ } + } + + body += '
\ + \ + '; + + response.bodyOutputStream.write(body, body.length); } +/** + * Sorts a and b (nsIFile objects) into an aesthetically pleasing order. + */ +function fileSort(a, b) +{ + var dira = a.isDirectory(), dirb = b.isDirectory(); + + if (dira && !dirb) + return -1; + if (dirb && !dira) + return 1; + + var namea = a.leafName.toLowerCase(), nameb = b.leafName.toLowerCase(); + return nameb > namea ? -1 : 1; +} + + /** * An object which handles requests for a server, executing default and * overridden behaviors as instructed by the code which uses and manipulates it. diff --git a/mozilla/netwerk/test/httpserver/test/test_default_index_handler.js b/mozilla/netwerk/test/httpserver/test/test_default_index_handler.js new file mode 100644 index 00000000000..69f64942246 --- /dev/null +++ b/mozilla/netwerk/test/httpserver/test/test_default_index_handler.js @@ -0,0 +1,268 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is MozJSHTTP code. + * + * The Initial Developer of the Original Code is + * Jeff Walden . + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// checks for correct output with the default index handler, mostly to do +// escaping checks -- highly dependent on the default index handler output +// format + +var paths = + [ + "http://localhost:4444/", // check top-level directory listing + "http://localhost:4444/foo/" // check non-top-level, too + ]; +var currPathIndex = 0; + +/** + * Verifies data in bytes (an array of bytes) represents an index page for the + * given URI and path, which should be a page listing the given directory + * entries, in order. + * + * @param bytes + * array of bytes representing the index page's contents + * @param uri + * string which is the URI of the index page + * @param path + * the path portion of uri + * @param dirEntries + * sorted (in the manner the directory entries should be sorted) array of + * objects, each of which has a name property (whose value is the file's name, + * without / if it's a directory) and an isDirectory property (with expected + * value) + */ +function dataCheck(bytes, uri, path, dirEntries) +{ + var data = String.fromCharCode.apply(null, bytes); + + var parser = Cc["@mozilla.org/xmlextras/domparser;1"] + .createInstance(Ci.nsIDOMParser); + + // Note: the index format isn't XML -- it's actually HTML -- but we require + // the index format also be valid XML, albeit XML without namespaces, + // XML declarations, etc. Doing this simplifies output checking. + try + { + var doc = parser.parseFromString(data, "application/xml"); + } + catch (e) + { + do_throw("document failed to parse as XML"); + } + + // See all the .QueryInterface()s and .item()s happening here? That's because + // xpcshell sucks and doesn't have classinfo, so no automatic interface + // flattening or array-style access to items in NodeLists. Suck. + + var body = doc.documentElement.getElementsByTagName("body"); + do_check_eq(body.length, 1); + body = body.item(0); + + // header + var header = body.QueryInterface(Ci.nsIDOMElement) + .getElementsByTagName("h1"); + do_check_eq(header.length, 1); + + do_check_eq(header.item(0).QueryInterface(Ci.nsIDOM3Node).textContent, path); + + // files + var lst = body.getElementsByTagName("ol"); + do_check_eq(lst.length, 1); + var items = lst.item(0).QueryInterface(Ci.nsIDOMElement) + .getElementsByTagName("li"); + + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + + var top = ios.newURI(uri, null, null); + + for (var i = 0; i < items.length; i++) + { + var link = items.item(i) + .childNodes + .item(0) + .QueryInterface(Ci.nsIDOM3Node) + .QueryInterface(Ci.nsIDOMElement); + var f = dirEntries[i]; + + var sep = f.isDirectory ? "/" : ""; + + do_check_eq(link.textContent, f.name + sep); + + var uri = ios.newURI(link.getAttribute("href"), null, top); + var fn = encodeURIComponent(f.name) + sep; + do_check_eq(uri.path, path + fn); + } +} + +var listener = + { + // data from request + _data: [], + + onStartRequest: function(request, cx) + { + var ch = request.QueryInterface(Ci.nsIHttpChannel); + do_check_eq(ch.getResponseHeader("Content-Type"), "text/html"); + + this._data.length = 0; + this._uri = paths[currPathIndex]; + this._path = this._uri.substring(paths[0].length - 1); + this._dirEntries = dirEntries[currPathIndex]; + }, + onDataAvailable: function(request, cx, inputStream, offset, count) + { + var bytes = makeBIS(inputStream).readByteArray(count); + Array.prototype.push.apply(this._data, bytes); + }, + onStopRequest: function(request, cx, status) + { + dataCheck(this._data, this._uri, this._path, this._dirEntries); + + if (++currPathIndex == paths.length) + { + srv.stop(); + destroyTestDirectory(); + } + else + { + performNextTest(); + } + do_test_finished(); + }, + QueryInterface: function(aIID) + { + if (aIID.equals(Ci.nsIStreamListener) || + aIID.equals(Ci.nsIRequestObserver) || + aIID.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + } + }; + + +var srv, dir, dirEntries; + +function run_test() +{ + createTestDirectory(); + + srv = createServer(); + srv.registerDirectory("/", dir); + srv.start(4444); + + performNextTest(); +} + +function performNextTest() +{ + do_test_pending(); + + var ch = makeChannel(paths[currPathIndex]); + ch.asyncOpen(listener, null); +} + +function createTestDirectory() +{ + dir = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("TmpD", Ci.nsIFile); + dir.append("index_handler_test_" + Math.random()); + dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0744); + + // populate with test directories, files, etc. + // Files must be in expected order of display on the index page! + + files = []; + + makeFile("aa_directory", true, dir, files); + makeFile("Ba_directory", true, dir, files); + makeFile("bb_directory", true, dir, files); + makeFile("foo", true, dir, files); + makeFile("a_file", false, dir, files); + makeFile("B_file", false, dir, files); + makeFile("za'z", false, dir, files); + makeFile("zb&z", false, dir, files); + makeFile("zcm", false, dir, files); + + dirEntries = [files]; + + var subdir = dir.clone(); + subdir.append("foo"); + + files = []; + + makeFile("aa_dir", true, subdir, files); + makeFile("b_dir", true, subdir, files); + makeFile("AA_file.txt", false, subdir, files); + makeFile("test.txt", false, subdir, files); + + dirEntries.push(files); +} + +function destroyTestDirectory() +{ + dir.remove(true); +} + + +// utilities + +/** + * Create a file/directory with the given name underneath parentDir, and + * append an object with name/isDirectory properties to lst corresponding + * to it if the file/directory could be created. + */ +function makeFile(name, isDirectory, parentDir, lst) +{ + var type = isDirectory + ? Ci.nsIFile.DIRECTORY_TYPE + : Ci.nsIFile.NORMAL_FILE_TYPE; + + var file = parentDir.clone(); + + try + { + file.append(name); + file.create(type, 0755); + lst.push({name: name, isDirectory: isDirectory}); + } + catch (e) { /* OS probably doesn't like file name, skip */ } +} diff --git a/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js b/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js index bb11e612c04..33185aa7268 100644 --- a/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js +++ b/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js @@ -66,7 +66,7 @@ var listener = break; case 1: - do_check_eq(ch.responseStatus, 500); + do_check_eq(ch.responseStatus, 200); // default index handler break; } }, @@ -99,13 +99,13 @@ function performNextTest() ch.asyncOpen(listener, null); } -var srv; +var srv, serverBasePath; function run_test() { srv = createServer(); - dirServ = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIProperties); + var dirServ = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); serverBasePath = dirServ.get("CurProcD", Ci.nsILocalFile); serverBasePath.append("httpserver_tests"); srv.registerDirectory("/", serverBasePath); @@ -119,5 +119,10 @@ function run_test() function myIndexHandler(metadata, response) { + var dir = metadata.getProperty("directory"); + do_check_true(dir != null); + do_check_true(dir instanceof Ci.nsIFile); + do_check_true(dir.equals(serverBasePath)); + response.write("directory!"); }