test_alt-data_closeWithStatus.js (4887B)
1 /** 2 * Test for the "alternative data stream" - closing the stream with an error. 3 * 4 * - we load a URL with preference for an alt data (check what we get is the raw data, 5 * since there was nothing previously cached) 6 * - we store something in alt data (using the asyncWait method) 7 * - then we abort the operation calling closeWithStatus() 8 * - we flush the HTTP cache 9 * - we reload the same URL using a new channel, again prefering the alt data be loaded 10 * - again we receive the data from the server. 11 */ 12 13 "use strict"; 14 15 const { HttpServer } = ChromeUtils.importESModule( 16 "resource://testing-common/httpd.sys.mjs" 17 ); 18 19 ChromeUtils.defineLazyGetter(this, "URL", function () { 20 return "http://localhost:" + httpServer.identity.primaryPort + "/content"; 21 }); 22 23 var httpServer = null; 24 25 // needs to be rooted 26 var cacheFlushObserver = (cacheFlushObserver = { 27 observe() { 28 cacheFlushObserver = null; 29 readServerContentAgain(); 30 }, 31 }); 32 33 var currentThread = null; 34 35 function make_channel(url) { 36 return NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true }); 37 } 38 39 function inChildProcess() { 40 return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; 41 } 42 43 const responseContent = "response body"; 44 const responseContent2 = "response body 2"; 45 const altContent = "!@#$%^&*()"; 46 const altContentType = "text/binary"; 47 48 var shouldPassRevalidation = true; 49 50 var cache_storage = null; 51 52 function contentHandler(metadata, response) { 53 response.setHeader("Content-Type", "text/plain"); 54 response.setHeader("Cache-Control", "no-cache"); 55 response.setHeader("ETag", "test-etag1"); 56 57 let etag; 58 try { 59 etag = metadata.getHeader("If-None-Match"); 60 } catch (ex) { 61 etag = ""; 62 } 63 64 if (etag == "test-etag1" && shouldPassRevalidation) { 65 response.setStatusLine(metadata.httpVersion, 304, "Not Modified"); 66 } else { 67 var content = shouldPassRevalidation ? responseContent : responseContent2; 68 response.bodyOutputStream.write(content, content.length); 69 } 70 } 71 72 function check_has_alt_data_in_index(aHasAltData, callback) { 73 if (inChildProcess()) { 74 callback(); 75 return; 76 } 77 78 syncWithCacheIOThread(() => { 79 var hasAltData = {}; 80 cache_storage.getCacheIndexEntryAttrs(createURI(URL), "", hasAltData, {}); 81 Assert.equal(hasAltData.value, aHasAltData); 82 callback(); 83 }, true); 84 } 85 86 function run_test() { 87 do_get_profile(); 88 httpServer = new HttpServer(); 89 httpServer.registerPathHandler("/content", contentHandler); 90 httpServer.start(-1); 91 do_test_pending(); 92 93 if (!inChildProcess()) { 94 cache_storage = getCacheStorage("disk"); 95 wait_for_cache_index(asyncOpen); 96 } else { 97 asyncOpen(); 98 } 99 } 100 101 function asyncOpen() { 102 var chan = make_channel(URL); 103 104 var cc = chan.QueryInterface(Ci.nsICacheInfoChannel); 105 cc.preferAlternativeDataType( 106 altContentType, 107 "", 108 Ci.nsICacheInfoChannel.ASYNC 109 ); 110 111 chan.asyncOpen(new ChannelListener(readServerContent, null)); 112 } 113 114 function readServerContent(request, buffer) { 115 var cc = request.QueryInterface(Ci.nsICacheInfoChannel); 116 117 Assert.equal(buffer, responseContent); 118 Assert.equal(cc.alternativeDataType, ""); 119 check_has_alt_data_in_index(false, () => { 120 if (!inChildProcess()) { 121 currentThread = Services.tm.currentThread; 122 } 123 124 executeSoon(() => { 125 var os = cc.openAlternativeOutputStream( 126 altContentType, 127 altContent.length 128 ); 129 130 var aos = os.QueryInterface(Ci.nsIAsyncOutputStream); 131 aos.asyncWait( 132 _ => { 133 os.write(altContent, altContent.length); 134 aos.closeWithStatus(Cr.NS_ERROR_FAILURE); 135 executeSoon(flushAndReadServerContentAgain); 136 }, 137 0, 138 0, 139 currentThread 140 ); 141 }); 142 }); 143 } 144 145 function flushAndReadServerContentAgain() { 146 // We need to do a GC pass to ensure the cache entry has been freed. 147 gc(); 148 if (!inChildProcess()) { 149 Services.cache2 150 .QueryInterface(Ci.nsICacheTesting) 151 .flush(cacheFlushObserver); 152 } else { 153 do_send_remote_message("flush"); 154 do_await_remote_message("flushed").then(() => { 155 readServerContentAgain(); 156 }); 157 } 158 } 159 160 function readServerContentAgain() { 161 var chan = make_channel(URL); 162 var cc = chan.QueryInterface(Ci.nsICacheInfoChannel); 163 cc.preferAlternativeDataType( 164 "dummy1", 165 "text/javascript", 166 Ci.nsICacheInfoChannel.ASYNC 167 ); 168 cc.preferAlternativeDataType( 169 altContentType, 170 "text/plain", 171 Ci.nsICacheInfoChannel.ASYNC 172 ); 173 cc.preferAlternativeDataType("dummy2", "", Ci.nsICacheInfoChannel.ASYNC); 174 175 chan.asyncOpen(new ChannelListener(readServerContentAgainCB, null)); 176 } 177 178 function readServerContentAgainCB(request, buffer) { 179 var cc = request.QueryInterface(Ci.nsICacheInfoChannel); 180 181 Assert.equal(buffer, responseContent); 182 Assert.equal(cc.alternativeDataType, ""); 183 check_has_alt_data_in_index(false, () => httpServer.stop(do_test_finished)); 184 }