Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Test the plaintext-or-binary sniffer |
michael@0 | 2 | |
michael@0 | 3 | Cu.import("resource://testing-common/httpd.js"); |
michael@0 | 4 | |
michael@0 | 5 | // List of Content-Type headers to test. For each header we have an array. |
michael@0 | 6 | // The first element in the array is the Content-Type header string. The |
michael@0 | 7 | // second element in the array is a boolean indicating whether we allow |
michael@0 | 8 | // sniffing for that type. |
michael@0 | 9 | var contentTypeHeaderList = |
michael@0 | 10 | [ |
michael@0 | 11 | [ "text/plain", true ], |
michael@0 | 12 | [ "text/plain; charset=ISO-8859-1", true ], |
michael@0 | 13 | [ "text/plain; charset=iso-8859-1", true ], |
michael@0 | 14 | [ "text/plain; charset=UTF-8", true ], |
michael@0 | 15 | [ "text/plain; charset=unknown", false ], |
michael@0 | 16 | [ "text/plain; param", false ], |
michael@0 | 17 | [ "text/plain; charset=ISO-8859-1; param", false ], |
michael@0 | 18 | [ "text/plain; charset=iso-8859-1; param", false ], |
michael@0 | 19 | [ "text/plain; charset=UTF-8; param", false ], |
michael@0 | 20 | [ "text/plain; charset=utf-8", false ], |
michael@0 | 21 | [ "text/plain; charset=utf8", false ], |
michael@0 | 22 | [ "text/plain; charset=UTF8", false ], |
michael@0 | 23 | [ "text/plain; charset=iSo-8859-1", false ] |
michael@0 | 24 | ]; |
michael@0 | 25 | |
michael@0 | 26 | // List of response bodies to test. For each response we have an array. The |
michael@0 | 27 | // first element in the array is the body string. The second element in the |
michael@0 | 28 | // array is a boolean indicating whether that string should sniff as binary. |
michael@0 | 29 | var bodyList = |
michael@0 | 30 | [ |
michael@0 | 31 | [ "Plaintext", false ] |
michael@0 | 32 | ]; |
michael@0 | 33 | |
michael@0 | 34 | // List of possible BOMs |
michael@0 | 35 | var BOMList = |
michael@0 | 36 | [ |
michael@0 | 37 | "\xFE\xFF", // UTF-16BE |
michael@0 | 38 | "\xFF\xFE", // UTF-16LE |
michael@0 | 39 | "\xEF\xBB\xBF", // UTF-8 |
michael@0 | 40 | "\x00\x00\xFE\xFF", // UCS-4BE |
michael@0 | 41 | "\x00\x00\xFF\xFE" // UCS-4LE |
michael@0 | 42 | ]; |
michael@0 | 43 | |
michael@0 | 44 | // Build up bodyList. The things we treat as binary are ASCII codes 0-8, |
michael@0 | 45 | // 14-26, 28-31. That is, the control char range, except for tab, newline, |
michael@0 | 46 | // vertical tab, form feed, carriage return, and ESC (this last being used by |
michael@0 | 47 | // Shift_JIS, apparently). |
michael@0 | 48 | function isBinaryChar(ch) { |
michael@0 | 49 | return (0 <= ch && ch <= 8) || (14 <= ch && ch <= 26) || |
michael@0 | 50 | (28 <= ch && ch <= 31); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | // Test chars on their own |
michael@0 | 54 | var i; |
michael@0 | 55 | for (i = 0; i <= 127; ++i) { |
michael@0 | 56 | bodyList.push([ String.fromCharCode(i), isBinaryChar(i) ]); |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | // Test that having a BOM prevents plaintext sniffing |
michael@0 | 60 | var j; |
michael@0 | 61 | for (i = 0; i <= 127; ++i) { |
michael@0 | 62 | for (j = 0; j < BOMList.length; ++j) { |
michael@0 | 63 | bodyList.push([ BOMList[j] + String.fromCharCode(i, i), false ]); |
michael@0 | 64 | } |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | // Test that having a BOM requires at least 4 chars to kick in |
michael@0 | 68 | for (i = 0; i <= 127; ++i) { |
michael@0 | 69 | for (j = 0; j < BOMList.length; ++j) { |
michael@0 | 70 | bodyList.push([ BOMList[j] + String.fromCharCode(i), |
michael@0 | 71 | BOMList[j].length == 2 && isBinaryChar(i) ]); |
michael@0 | 72 | } |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | function makeChan(headerIdx, bodyIdx) { |
michael@0 | 76 | var ios = Components.classes["@mozilla.org/network/io-service;1"] |
michael@0 | 77 | .getService(Components.interfaces.nsIIOService); |
michael@0 | 78 | var chan = |
michael@0 | 79 | ios.newChannel("http://localhost:" + httpserv.identity.primaryPort + |
michael@0 | 80 | "/" + headerIdx + "/" + bodyIdx, null, null) |
michael@0 | 81 | .QueryInterface(Components.interfaces.nsIHttpChannel); |
michael@0 | 82 | |
michael@0 | 83 | chan.loadFlags |= |
michael@0 | 84 | Components.interfaces.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS; |
michael@0 | 85 | |
michael@0 | 86 | return chan; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | function makeListener(headerIdx, bodyIdx) { |
michael@0 | 90 | var listener = { |
michael@0 | 91 | onStartRequest : function test_onStartR(request, ctx) { |
michael@0 | 92 | try { |
michael@0 | 93 | var chan = request.QueryInterface(Components.interfaces.nsIChannel); |
michael@0 | 94 | |
michael@0 | 95 | do_check_eq(chan.status, Components.results.NS_OK); |
michael@0 | 96 | |
michael@0 | 97 | var type = chan.contentType; |
michael@0 | 98 | |
michael@0 | 99 | var expectedType = |
michael@0 | 100 | contentTypeHeaderList[headerIdx][1] && bodyList[bodyIdx][1] ? |
michael@0 | 101 | "application/x-vnd.mozilla.guess-from-ext" : "text/plain"; |
michael@0 | 102 | if (expectedType != type) { |
michael@0 | 103 | do_throw("Unexpected sniffed type '" + type + "'. " + |
michael@0 | 104 | "Should be '" + expectedType + "'. " + |
michael@0 | 105 | "Header is ['" + |
michael@0 | 106 | contentTypeHeaderList[headerIdx][0] + "', " + |
michael@0 | 107 | contentTypeHeaderList[headerIdx][1] + "]. " + |
michael@0 | 108 | "Body is ['" + |
michael@0 | 109 | bodyList[bodyIdx][0].toSource() + "', " + |
michael@0 | 110 | bodyList[bodyIdx][1] + |
michael@0 | 111 | "]."); |
michael@0 | 112 | } |
michael@0 | 113 | do_check_eq(expectedType, type); |
michael@0 | 114 | } catch (e) { |
michael@0 | 115 | do_throw("Unexpected exception: " + e); |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | throw Components.results.NS_ERROR_ABORT; |
michael@0 | 119 | }, |
michael@0 | 120 | |
michael@0 | 121 | onDataAvailable: function test_ODA() { |
michael@0 | 122 | do_throw("Should not get any data!"); |
michael@0 | 123 | }, |
michael@0 | 124 | |
michael@0 | 125 | onStopRequest: function test_onStopR(request, ctx, status) { |
michael@0 | 126 | // Advance to next test |
michael@0 | 127 | ++headerIdx; |
michael@0 | 128 | if (headerIdx == contentTypeHeaderList.length) { |
michael@0 | 129 | headerIdx = 0; |
michael@0 | 130 | ++bodyIdx; |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | if (bodyIdx == bodyList.length) { |
michael@0 | 134 | do_test_pending(); |
michael@0 | 135 | httpserv.stop(do_test_finished); |
michael@0 | 136 | } else { |
michael@0 | 137 | doTest(headerIdx, bodyIdx); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | do_test_finished(); |
michael@0 | 141 | } |
michael@0 | 142 | }; |
michael@0 | 143 | |
michael@0 | 144 | return listener; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | function doTest(headerIdx, bodyIdx) { |
michael@0 | 148 | var chan = makeChan(headerIdx, bodyIdx); |
michael@0 | 149 | |
michael@0 | 150 | var listener = makeListener(headerIdx, bodyIdx); |
michael@0 | 151 | |
michael@0 | 152 | chan.asyncOpen(listener, null); |
michael@0 | 153 | |
michael@0 | 154 | do_test_pending(); |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | function createResponse(headerIdx, bodyIdx, metadata, response) { |
michael@0 | 158 | response.setHeader("Content-Type", contentTypeHeaderList[headerIdx][0], false); |
michael@0 | 159 | response.bodyOutputStream.write(bodyList[bodyIdx][0], |
michael@0 | 160 | bodyList[bodyIdx][0].length); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | function makeHandler(headerIdx, bodyIdx) { |
michael@0 | 164 | var f = |
michael@0 | 165 | function handlerClosure(metadata, response) { |
michael@0 | 166 | return createResponse(headerIdx, bodyIdx, metadata, response); |
michael@0 | 167 | }; |
michael@0 | 168 | return f; |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | var httpserv; |
michael@0 | 172 | function run_test() { |
michael@0 | 173 | // disable again for everything for now (causes sporatic oranges) |
michael@0 | 174 | return; |
michael@0 | 175 | |
michael@0 | 176 | // disable on Windows for now, because it seems to leak sockets and die. |
michael@0 | 177 | // Silly operating system! |
michael@0 | 178 | // This is a really nasty way to detect Windows. I wish we could do better. |
michael@0 | 179 | if ("@mozilla.org/windows-registry-key;1" in Cc) { |
michael@0 | 180 | return; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | httpserv = new HttpServer(); |
michael@0 | 184 | |
michael@0 | 185 | for (i = 0; i < contentTypeHeaderList.length; ++i) { |
michael@0 | 186 | for (j = 0; j < bodyList.length; ++j) { |
michael@0 | 187 | httpserv.registerPathHandler("/" + i + "/" + j, makeHandler(i, j)); |
michael@0 | 188 | } |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | httpserv.start(-1); |
michael@0 | 192 | |
michael@0 | 193 | doTest(0, 0); |
michael@0 | 194 | } |