test_content_length_underrun.js (9981B)
1 /* 2 * Test Content-Length underrun behavior 3 */ 4 5 //////////////////////////////////////////////////////////////////////////////// 6 // Test infrastructure 7 8 "use strict"; 9 10 const { HttpServer } = ChromeUtils.importESModule( 11 "resource://testing-common/httpd.sys.mjs" 12 ); 13 14 ChromeUtils.defineLazyGetter(this, "URL", function () { 15 return "http://localhost:" + httpserver.identity.primaryPort; 16 }); 17 18 var httpserver = new HttpServer(); 19 var test_flags = []; 20 var testPathBase = "/cl_hdrs"; 21 22 var prefs; 23 var enforcePrefStrict; 24 var enforcePrefSoft; 25 var enforcePrefStrictChunked; 26 27 Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); 28 registerCleanupFunction(() => { 29 Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); 30 }); 31 32 function run_test() { 33 prefs = Services.prefs; 34 enforcePrefStrict = prefs.getBoolPref("network.http.enforce-framing.http1"); 35 enforcePrefSoft = prefs.getBoolPref("network.http.enforce-framing.soft"); 36 enforcePrefStrictChunked = prefs.getBoolPref( 37 "network.http.enforce-framing.strict_chunked_encoding" 38 ); 39 40 prefs.setBoolPref("network.http.enforce-framing.http1", true); 41 42 httpserver.start(-1); 43 44 do_test_pending(); 45 run_test_number(1); 46 } 47 48 function run_test_number(num) { 49 let testPath = testPathBase + num; 50 // eslint-disable-next-line no-eval 51 httpserver.registerPathHandler(testPath, eval("handler" + num)); 52 53 var channel = setupChannel(testPath); 54 let flags = test_flags[num]; // OK if flags undefined for test 55 channel.asyncOpen( 56 // eslint-disable-next-line no-eval 57 new ChannelListener(eval("completeTest" + num), channel, flags) 58 ); 59 } 60 61 function run_gzip_test(num) { 62 let testPath = testPathBase + num; 63 // eslint-disable-next-line no-eval 64 httpserver.registerPathHandler(testPath, eval("handler" + num)); 65 66 var channel = setupChannel(testPath); 67 68 function StreamListener() {} 69 70 StreamListener.prototype = { 71 QueryInterface: ChromeUtils.generateQI([ 72 "nsIStreamListener", 73 "nsIRequestObserver", 74 ]), 75 76 onStartRequest() {}, 77 78 onStopRequest(aRequest, aStatusCode) { 79 // Make sure we catch the error NS_ERROR_NET_PARTIAL_TRANSFER here. 80 Assert.equal(aStatusCode, Cr.NS_ERROR_NET_PARTIAL_TRANSFER); 81 // do_test_finished(); 82 endTests(); 83 }, 84 85 onDataAvailable() {}, 86 }; 87 88 let listener = new StreamListener(); 89 90 channel.asyncOpen(listener); 91 } 92 93 function setupChannel(url) { 94 var chan = NetUtil.newChannel({ 95 uri: URL + url, 96 loadUsingSystemPrincipal: true, 97 }); 98 var httpChan = chan.QueryInterface(Ci.nsIHttpChannel); 99 return httpChan; 100 } 101 102 function endTests() { 103 // restore the prefs to pre-test values 104 prefs.setBoolPref("network.http.enforce-framing.http1", enforcePrefStrict); 105 prefs.setBoolPref("network.http.enforce-framing.soft", enforcePrefSoft); 106 prefs.setBoolPref( 107 "network.http.enforce-framing.strict_chunked_encoding", 108 enforcePrefStrictChunked 109 ); 110 httpserver.stop(do_test_finished); 111 } 112 113 //////////////////////////////////////////////////////////////////////////////// 114 // Test 1: FAIL because of Content-Length underrun with HTTP 1.1 115 test_flags[1] = CL_EXPECT_LATE_FAILURE; 116 117 // eslint-disable-next-line no-unused-vars 118 function handler1(metadata, response) { 119 var body = "blablabla"; 120 121 response.seizePower(); 122 response.write("HTTP/1.1 200 OK\r\n"); 123 response.write("Content-Type: text/plain\r\n"); 124 response.write("Content-Length: 556677\r\n"); 125 response.write("\r\n"); 126 response.write(body); 127 response.finish(); 128 } 129 130 // eslint-disable-next-line no-unused-vars 131 function completeTest1(request) { 132 Assert.equal(request.status, Cr.NS_ERROR_NET_PARTIAL_TRANSFER); 133 134 run_test_number(11); 135 } 136 137 //////////////////////////////////////////////////////////////////////////////// 138 // Test 11: PASS because of Content-Length underrun with HTTP 1.1 but non 2xx 139 test_flags[11] = CL_IGNORE_CL; 140 141 // eslint-disable-next-line no-unused-vars 142 function handler11(metadata, response) { 143 var body = "blablabla"; 144 145 response.seizePower(); 146 response.write("HTTP/1.1 404 NotOK\r\n"); 147 response.write("Content-Type: text/plain\r\n"); 148 response.write("Content-Length: 556677\r\n"); 149 response.write("\r\n"); 150 response.write(body); 151 response.finish(); 152 } 153 154 // eslint-disable-next-line no-unused-vars 155 function completeTest11(request) { 156 Assert.equal(request.status, Cr.NS_OK); 157 run_test_number(2); 158 } 159 160 //////////////////////////////////////////////////////////////////////////////// 161 // Test 2: Succeed because Content-Length underrun is with HTTP 1.0 162 163 test_flags[2] = CL_IGNORE_CL; 164 165 // eslint-disable-next-line no-unused-vars 166 function handler2(metadata, response) { 167 var body = "short content"; 168 169 response.seizePower(); 170 response.write("HTTP/1.0 200 OK\r\n"); 171 response.write("Content-Type: text/plain\r\n"); 172 response.write("Content-Length: 12345678\r\n"); 173 response.write("\r\n"); 174 response.write(body); 175 response.finish(); 176 } 177 178 // eslint-disable-next-line no-unused-vars 179 function completeTest2(request) { 180 Assert.equal(request.status, Cr.NS_OK); 181 182 // test 3 requires the enforce-framing prefs to be false 183 prefs.setBoolPref("network.http.enforce-framing.http1", false); 184 prefs.setBoolPref("network.http.enforce-framing.soft", false); 185 prefs.setBoolPref( 186 "network.http.enforce-framing.strict_chunked_encoding", 187 false 188 ); 189 run_test_number(3); 190 } 191 192 //////////////////////////////////////////////////////////////////////////////// 193 // Test 3: SUCCEED with bad Content-Length because pref allows it 194 test_flags[3] = CL_IGNORE_CL; 195 196 // eslint-disable-next-line no-unused-vars 197 function handler3(metadata, response) { 198 var body = "blablabla"; 199 200 response.seizePower(); 201 response.write("HTTP/1.1 200 OK\r\n"); 202 response.write("Content-Type: text/plain\r\n"); 203 response.write("Content-Length: 556677\r\n"); 204 response.write("\r\n"); 205 response.write(body); 206 response.finish(); 207 } 208 209 // eslint-disable-next-line no-unused-vars 210 function completeTest3(request) { 211 Assert.equal(request.status, Cr.NS_OK); 212 prefs.setBoolPref("network.http.enforce-framing.soft", true); 213 run_test_number(4); 214 } 215 216 //////////////////////////////////////////////////////////////////////////////// 217 // Test 4: Succeed because a cut off deflate stream can't be detected 218 test_flags[4] = CL_IGNORE_CL; 219 220 // eslint-disable-next-line no-unused-vars 221 function handler4(metadata, response) { 222 // this is the beginning of a deflate compressed response body 223 224 var body = 225 "\xcd\x57\xcd\x6e\x1b\x37\x10\xbe\x07\xc8\x3b\x0c\x36\x68\x72\xd1" + 226 "\xbf\x92\x22\xb1\x57\x0a\x64\x4b\x6a\x0c\x28\xb6\x61\xa9\x41\x73" + 227 "\x2a\xb8\xbb\x94\x44\x98\xfb\x03\x92\x92\xec\x06\x7d\x97\x1e\xeb" + 228 "\xbe\x86\x5e\xac\xc3\x25\x97\xa2\x64\xb9\x75\x0b\x14\xe8\x69\x87" + 229 "\x33\x9c\x1f\x7e\x33\x9c\xe1\x86\x9f\x66\x9f\x27\xfd\x97\x2f\x20" + 230 "\xfc\x34\x1a\x0c\x35\x01\xa1\x62\x8a\xd3\xfe\xf5\xcd\xd5\xe5\xd5" + 231 "\x6c\x54\x83\x49\xbe\x60\x31\xa3\x1c\x12\x0a\x0b\x2a\x15\xcb\x33" + 232 "\x4d\xae\x19\x05\x19\xe7\x9c\x30\x41\x1b\x61\xd3\x28\x95\xfa\x29" + 233 "\x55\x04\x32\x92\xd2\x5e\x90\x50\x19\x0b\x56\x68\x9d\x00\xe2\x3c" + 234 "\x53\x34\x53\xbd\xc0\x99\x56\xf9\x4a\x51\xe0\x64\xcf\x18\x24\x24" + 235 "\x93\xb0\xca\x40\xd2\x15\x07\x6e\xbd\x37\x60\x82\x3b\x8f\x86\x22" + 236 "\x21\xcb\x15\x95\x35\x20\x91\xa4\x59\xac\xa9\x62\x95\x31\xed\x14" + 237 "\xc9\x98\x2c\x19\x15\x3a\x62\x45\xef\x70\x1b\x50\x05\xa4\x28\xc4" + 238 "\xf6\x21\x66\xa4\xdc\x83\x32\x09\x85\xc8\xe7\x54\xa2\x4b\x81\x74" + 239 "\xbe\x12\xc0\x91\xb9\x7d\x50\x24\xe2\x0c\xd9\x29\x06\x2e\xdd\x79"; 240 241 response.seizePower(); 242 response.write("HTTP/1.1 200 OK\r\n"); 243 response.write("Content-Type: text/plain\r\n"); 244 response.write("Content-Length: 553677\r\n"); 245 response.write("Content-Encoding: deflate\r\n"); 246 response.write("\r\n"); 247 response.write(body); 248 response.finish(); 249 } 250 251 // eslint-disable-next-line no-unused-vars 252 function completeTest4(request) { 253 Assert.equal(request.status, Cr.NS_OK); 254 255 prefs.setBoolPref("network.http.enforce-framing.http1", true); 256 run_gzip_test(99); 257 } 258 259 //////////////////////////////////////////////////////////////////////////////// 260 // Test 99: FAIL because a cut off gzip stream CAN be detected 261 262 // Note that test 99 here is run completely different than the other tests in 263 // this file so if you add more tests here, consider adding them before this. 264 265 // eslint-disable-next-line no-unused-vars 266 function handler99(metadata, response) { 267 // this is the beginning of a gzip compressed response body 268 269 var body = 270 "\x1f\x8b\x08\x00\x80\xb9\x25\x53\x00\x03\xd4\xd9\x79\xb8\x8e\xe5" + 271 "\xba\x00\xf0\x65\x19\x33\x24\x15\x29\xf3\x50\x52\xc6\xac\x85\x10" + 272 "\x8b\x12\x22\x45\xe6\xb6\x21\x9a\x96\x84\x4c\x69\x32\xec\x84\x92" + 273 "\xcc\x99\x6a\xd9\x32\xa5\xd0\x40\xd9\xc6\x14\x15\x95\x28\x62\x9b" + 274 "\x09\xc9\x70\x4a\x25\x53\xec\x8e\x9c\xe5\x1c\x9d\xeb\xfe\x9d\x73" + 275 "\x9d\x3f\xf6\x1f\xe7\xbd\xae\xcf\xf3\xbd\xbf\xef\x7e\x9f\xeb\x79" + 276 "\xef\xf7\x99\xde\xe5\xee\x6e\xdd\x3b\x75\xeb\xd1\xb5\x6c\xb3\xd4" + 277 "\x47\x1f\x48\xf8\x17\x1d\x15\xce\x1d\x55\x92\x93\xcf\x97\xe7\x8e" + 278 "\x8b\xca\xe4\xca\x55\x92\x2a\x54\x4e\x4e\x4e\x4a\xa8\x78\x53\xa5" + 279 "\x8a\x15\x2b\x55\x4a\xfa\xe3\x7b\x85\x8a\x37\x55\x48\xae\x92\x50" + 280 "\xb4\xc2\xbf\xaa\x41\x17\x1f\xbd\x7b\xf6\xba\xaf\x47\xd1\xa2\x09" + 281 "\x3d\xba\x75\xeb\xf5\x3f\xc5\xfd\x6f\xbf\xff\x3f\x3d\xfa\xd7\x6d" + 282 "\x74\x7b\x62\x86\x0c\xff\x79\x9e\x98\x50\x33\xe1\x8f\xb3\x01\xef" + 283 "\xb6\x38\x7f\x9e\x92\xee\xf9\xa7\xee\xcb\x74\x21\x26\x25\xa1\x6a" + 284 "\x42\xf6\x73\xff\x96\x4c\x28\x91\x90\xe5\xdc\x79\xa6\x8b\xe2\x52" + 285 "\xd2\xbf\x5d\x28\x2b\x24\x26\xfc\xa9\xcc\x96\x1e\x97\x31\xfd\xba" + 286 "\xee\xe9\xde\x3d\x31\xe5\x4f\x65\xc1\xf4\xb8\x0b\x65\x86\x8b\xca"; 287 response.seizePower(); 288 response.write("HTTP/1.1 200 OK\r\n"); 289 response.write("Content-Type: text/plain\r\n"); 290 response.write("Content-Length: 553677\r\n"); 291 response.write("Content-Encoding: gzip\r\n"); 292 response.write("\r\n"); 293 response.write(body); 294 response.finish(); 295 }