michael@0: // Test the plaintext-or-binary sniffer michael@0: michael@0: Cu.import("resource://testing-common/httpd.js"); michael@0: michael@0: // List of Content-Type headers to test. For each header we have an array. michael@0: // The first element in the array is the Content-Type header string. The michael@0: // second element in the array is a boolean indicating whether we allow michael@0: // sniffing for that type. michael@0: var contentTypeHeaderList = michael@0: [ michael@0: [ "text/plain", true ], michael@0: [ "text/plain; charset=ISO-8859-1", true ], michael@0: [ "text/plain; charset=iso-8859-1", true ], michael@0: [ "text/plain; charset=UTF-8", true ], michael@0: [ "text/plain; charset=unknown", false ], michael@0: [ "text/plain; param", false ], michael@0: [ "text/plain; charset=ISO-8859-1; param", false ], michael@0: [ "text/plain; charset=iso-8859-1; param", false ], michael@0: [ "text/plain; charset=UTF-8; param", false ], michael@0: [ "text/plain; charset=utf-8", false ], michael@0: [ "text/plain; charset=utf8", false ], michael@0: [ "text/plain; charset=UTF8", false ], michael@0: [ "text/plain; charset=iSo-8859-1", false ] michael@0: ]; michael@0: michael@0: // List of response bodies to test. For each response we have an array. The michael@0: // first element in the array is the body string. The second element in the michael@0: // array is a boolean indicating whether that string should sniff as binary. michael@0: var bodyList = michael@0: [ michael@0: [ "Plaintext", false ] michael@0: ]; michael@0: michael@0: // List of possible BOMs michael@0: var BOMList = michael@0: [ michael@0: "\xFE\xFF", // UTF-16BE michael@0: "\xFF\xFE", // UTF-16LE michael@0: "\xEF\xBB\xBF", // UTF-8 michael@0: "\x00\x00\xFE\xFF", // UCS-4BE michael@0: "\x00\x00\xFF\xFE" // UCS-4LE michael@0: ]; michael@0: michael@0: // Build up bodyList. The things we treat as binary are ASCII codes 0-8, michael@0: // 14-26, 28-31. That is, the control char range, except for tab, newline, michael@0: // vertical tab, form feed, carriage return, and ESC (this last being used by michael@0: // Shift_JIS, apparently). michael@0: function isBinaryChar(ch) { michael@0: return (0 <= ch && ch <= 8) || (14 <= ch && ch <= 26) || michael@0: (28 <= ch && ch <= 31); michael@0: } michael@0: michael@0: // Test chars on their own michael@0: var i; michael@0: for (i = 0; i <= 127; ++i) { michael@0: bodyList.push([ String.fromCharCode(i), isBinaryChar(i) ]); michael@0: } michael@0: michael@0: // Test that having a BOM prevents plaintext sniffing michael@0: var j; michael@0: for (i = 0; i <= 127; ++i) { michael@0: for (j = 0; j < BOMList.length; ++j) { michael@0: bodyList.push([ BOMList[j] + String.fromCharCode(i, i), false ]); michael@0: } michael@0: } michael@0: michael@0: // Test that having a BOM requires at least 4 chars to kick in michael@0: for (i = 0; i <= 127; ++i) { michael@0: for (j = 0; j < BOMList.length; ++j) { michael@0: bodyList.push([ BOMList[j] + String.fromCharCode(i), michael@0: BOMList[j].length == 2 && isBinaryChar(i) ]); michael@0: } michael@0: } michael@0: michael@0: function makeChan(headerIdx, bodyIdx) { michael@0: var ios = Components.classes["@mozilla.org/network/io-service;1"] michael@0: .getService(Components.interfaces.nsIIOService); michael@0: var chan = michael@0: ios.newChannel("http://localhost:" + httpserv.identity.primaryPort + michael@0: "/" + headerIdx + "/" + bodyIdx, null, null) michael@0: .QueryInterface(Components.interfaces.nsIHttpChannel); michael@0: michael@0: chan.loadFlags |= michael@0: Components.interfaces.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS; michael@0: michael@0: return chan; michael@0: } michael@0: michael@0: function makeListener(headerIdx, bodyIdx) { michael@0: var listener = { michael@0: onStartRequest : function test_onStartR(request, ctx) { michael@0: try { michael@0: var chan = request.QueryInterface(Components.interfaces.nsIChannel); michael@0: michael@0: do_check_eq(chan.status, Components.results.NS_OK); michael@0: michael@0: var type = chan.contentType; michael@0: michael@0: var expectedType = michael@0: contentTypeHeaderList[headerIdx][1] && bodyList[bodyIdx][1] ? michael@0: "application/x-vnd.mozilla.guess-from-ext" : "text/plain"; michael@0: if (expectedType != type) { michael@0: do_throw("Unexpected sniffed type '" + type + "'. " + michael@0: "Should be '" + expectedType + "'. " + michael@0: "Header is ['" + michael@0: contentTypeHeaderList[headerIdx][0] + "', " + michael@0: contentTypeHeaderList[headerIdx][1] + "]. " + michael@0: "Body is ['" + michael@0: bodyList[bodyIdx][0].toSource() + "', " + michael@0: bodyList[bodyIdx][1] + michael@0: "]."); michael@0: } michael@0: do_check_eq(expectedType, type); michael@0: } catch (e) { michael@0: do_throw("Unexpected exception: " + e); michael@0: } michael@0: michael@0: throw Components.results.NS_ERROR_ABORT; michael@0: }, michael@0: michael@0: onDataAvailable: function test_ODA() { michael@0: do_throw("Should not get any data!"); michael@0: }, michael@0: michael@0: onStopRequest: function test_onStopR(request, ctx, status) { michael@0: // Advance to next test michael@0: ++headerIdx; michael@0: if (headerIdx == contentTypeHeaderList.length) { michael@0: headerIdx = 0; michael@0: ++bodyIdx; michael@0: } michael@0: michael@0: if (bodyIdx == bodyList.length) { michael@0: do_test_pending(); michael@0: httpserv.stop(do_test_finished); michael@0: } else { michael@0: doTest(headerIdx, bodyIdx); michael@0: } michael@0: michael@0: do_test_finished(); michael@0: } michael@0: }; michael@0: michael@0: return listener; michael@0: } michael@0: michael@0: function doTest(headerIdx, bodyIdx) { michael@0: var chan = makeChan(headerIdx, bodyIdx); michael@0: michael@0: var listener = makeListener(headerIdx, bodyIdx); michael@0: michael@0: chan.asyncOpen(listener, null); michael@0: michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function createResponse(headerIdx, bodyIdx, metadata, response) { michael@0: response.setHeader("Content-Type", contentTypeHeaderList[headerIdx][0], false); michael@0: response.bodyOutputStream.write(bodyList[bodyIdx][0], michael@0: bodyList[bodyIdx][0].length); michael@0: } michael@0: michael@0: function makeHandler(headerIdx, bodyIdx) { michael@0: var f = michael@0: function handlerClosure(metadata, response) { michael@0: return createResponse(headerIdx, bodyIdx, metadata, response); michael@0: }; michael@0: return f; michael@0: } michael@0: michael@0: var httpserv; michael@0: function run_test() { michael@0: // disable again for everything for now (causes sporatic oranges) michael@0: return; michael@0: michael@0: // disable on Windows for now, because it seems to leak sockets and die. michael@0: // Silly operating system! michael@0: // This is a really nasty way to detect Windows. I wish we could do better. michael@0: if ("@mozilla.org/windows-registry-key;1" in Cc) { michael@0: return; michael@0: } michael@0: michael@0: httpserv = new HttpServer(); michael@0: michael@0: for (i = 0; i < contentTypeHeaderList.length; ++i) { michael@0: for (j = 0; j < bodyList.length; ++j) { michael@0: httpserv.registerPathHandler("/" + i + "/" + j, makeHandler(i, j)); michael@0: } michael@0: } michael@0: michael@0: httpserv.start(-1); michael@0: michael@0: doTest(0, 0); michael@0: }