network-partition-key.html (8987B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Connection partitioning by site</title> 6 <meta name="help" href="https://fetch.spec.whatwg.org/#network-partition-keys"> 7 <meta name="timeout" content="long"> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/common/utils.js"></script> 11 <script src="/common/get-host-info.sub.js"></script> 12 </head> 13 <body> 14 <!-- Used to open about:blank tabs from opaque origins --> 15 <iframe id="iframe0" sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"></iframe> 16 <iframe id="iframe1" sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"></iframe> 17 <script> 18 const host = get_host_info(); 19 20 // These two origins must correspond to different sites for this test to pass. 21 const POPUP_ORIGINS = [ 22 host.ORIGIN, 23 host.HTTP_NOTSAMESITE_ORIGIN 24 ]; 25 26 // This origin should ideally correspond to a different site from the two above, but the 27 // tests will still pass if it matches the site of one of the other two origins. 28 const OTHER_ORIGIN = host.REMOTE_ORIGIN; 29 30 // Except for the csp_sandbox and about:blanks, each test opens up two windows, one at 31 // POPUP_ORIGINS[0], one at POPUP_ORIGINS[1], and has them request subresources from 32 // subresource_origin. All requests (HTML, JS, and fetch requests) for each window go 33 // through network-partition-key.py and have a partition_id parameter, which is used 34 // to check if any request for one window uses the same socket as a request for the 35 // other window. 36 // 37 // Whenever requests from the two different popup windows use the same connection, the 38 // fetch requests all start returning 400 errors, but other requests will continue to 39 // succeed, to make for clearer errors. 40 // 41 // include_credentials indicates whether the fetch requests use credentials or not, 42 // which is interesting as uncredentialed sockets have separate connection pools. 43 const tests = [ 44 { 45 name: 'With credentials', 46 subresource_origin: POPUP_ORIGINS[0], 47 include_credentials: true, 48 popup_params: [ 49 {type: 'main_frame'}, 50 {type: 'main_frame'} 51 ] 52 }, 53 { 54 name: 'Without credentials', 55 subresource_origin: POPUP_ORIGINS[0], 56 include_credentials: false, 57 popup_params: [ 58 {type: 'main_frame'}, 59 {type: 'main_frame'} 60 ] 61 }, 62 { 63 name: 'Cross-site resources with credentials', 64 subresource_origin: OTHER_ORIGIN, 65 include_credentials: true, 66 popup_params: [ 67 {type: 'main_frame'}, 68 {type: 'main_frame'} 69 ] 70 }, 71 { 72 name: 'Cross-site resources without credentials', 73 subresource_origin: OTHER_ORIGIN, 74 include_credentials: false, 75 popup_params: [ 76 {type: 'main_frame'}, 77 {type: 'main_frame'} 78 ] 79 }, 80 { 81 name: 'Iframes', 82 subresource_origin: OTHER_ORIGIN, 83 include_credentials: true, 84 popup_params: [ 85 { 86 type: 'iframe', 87 iframe_origin: OTHER_ORIGIN 88 }, 89 { 90 type: 'iframe', 91 iframe_origin: OTHER_ORIGIN 92 } 93 ] 94 }, 95 { 96 name: 'Workers', 97 subresource_origin: POPUP_ORIGINS[0], 98 include_credentials: true, 99 popup_params: [ 100 {type: 'worker'}, 101 {type: 'worker'} 102 ] 103 }, 104 { 105 name: 'Workers with cross-site resources', 106 subresource_origin: OTHER_ORIGIN, 107 include_credentials: true, 108 popup_params: [ 109 {type: 'worker'}, 110 {type: 'worker'} 111 ] 112 }, 113 { 114 name: 'CSP sandbox', 115 subresource_origin: POPUP_ORIGINS[0], 116 include_credentials: true, 117 popup_params: [ 118 {type: 'csp_sandbox'}, 119 {type: 'csp_sandbox'} 120 ] 121 }, 122 { 123 name: 'about:blank from opaque origin iframe', 124 subresource_origin: OTHER_ORIGIN, 125 include_credentials: true, 126 popup_params: [ 127 {type: 'opaque_about_blank'}, 128 {type: 'opaque_about_blank'} 129 ] 130 }, 131 ]; 132 133 const BASE_PATH = window.location.pathname.replace(/\/[^\/]*$/, '/'); 134 135 function create_script_url(origin, uuid, partition_id, dispatch) { 136 return `${origin}${BASE_PATH}resources/network-partition-key.py?uuid=${uuid}&partition_id=${partition_id}&dispatch=${dispatch}` 137 } 138 139 function run_test(test) { 140 var uuid = token(); 141 142 // Used to track the opened popup windows, so they can be closed at the end of the test. 143 // They could be closed immediately after use, but safest to keep them open, as browsers 144 // could use closing a window as a hint to close idle sockets that the window used. 145 var popup_windows = []; 146 147 // Creates a popup window at |url| and waits for a test result. Returns a promise. 148 function create_popup_and_wait_for_result(url) { 149 return new Promise(function(resolve, reject) { 150 popup_windows.push(window.open(url)); 151 // Listen for the result 152 function message_listener(event) { 153 if (event.data.result === 'success') { 154 resolve(); 155 } else if (event.data.result === 'error') { 156 reject(event.data.details); 157 } else { 158 reject('Unexpected message.'); 159 } 160 } 161 window.addEventListener('message', message_listener, {once: 'true'}); 162 }); 163 } 164 165 // Navigates iframe to url and waits for a test result. Returns a promise. 166 function navigate_iframe_and_wait_for_result(iframe, url) { 167 return new Promise(function(resolve, reject) { 168 iframe.src = url; 169 // Listen for the result 170 function message_listener(event) { 171 if (event.data.result === 'success') { 172 resolve(); 173 } else if (event.data.result === 'error') { 174 reject(event.data.details); 175 } else { 176 reject('Unexpected message.'); 177 } 178 } 179 window.addEventListener('message', message_listener, {once: 'true'}); 180 }); 181 } 182 183 function make_test_function(test, index) { 184 var popup_params = test.popup_params[index]; 185 return function() { 186 var popup_path; 187 var additional_url_params = ''; 188 var origin = POPUP_ORIGINS[index]; 189 var partition_id = POPUP_ORIGINS[index]; 190 if (popup_params.type == 'main_frame') { 191 popup_path = 'resources/network-partition-checker.html'; 192 } else if (popup_params.type == 'iframe') { 193 popup_path = 'resources/network-partition-iframe-checker.html'; 194 additional_url_params = `&other_origin=${popup_params.iframe_origin}`; 195 } else if (popup_params.type == 'worker') { 196 popup_path = 'resources/network-partition-worker-checker.html'; 197 // The origin of the dedicated worker must mutch the page that loads it. 198 additional_url_params = `&other_origin=${POPUP_ORIGINS[index]}`; 199 } else if (popup_params.type == 'csp_sandbox') { 200 // For the Content-Security-Policy sandbox test, all requests are from the same origin, but 201 // the origin should be treated as an opaque origin, so sockets should not be reused. 202 origin = test.subresource_origin; 203 partition_id = index; 204 popup_path = 'resources/network-partition-checker.html'; 205 // Don't check partition of root document, since the document isn't sandboxed until the 206 // root document is fetched. 207 additional_url_params = '&sandbox=true&nocheck_partition=true' 208 } else if (popup_params.type=='opaque_about_blank') { 209 popup_path = 'resources/network-partition-about-blank-checker.html'; 210 } else if (popup_params.type == 'iframe') { 211 throw 'Unrecognized popup_params.type.'; 212 } 213 var url = create_script_url(origin, uuid, partition_id, 'fetch_file'); 214 url += `&subresource_origin=${test.subresource_origin}` 215 url += `&include_credentials=${test.include_credentials}` 216 url += `&path=${BASE_PATH.substring(1)}${popup_path}`; 217 url += additional_url_params; 218 219 if (popup_params.type=='opaque_about_blank') { 220 return navigate_iframe_and_wait_for_result(iframe = document.getElementById('iframe' + index), url); 221 } 222 223 return create_popup_and_wait_for_result(url); 224 } 225 } 226 227 // Takes a Promise, and cleans up state when the promise has completed, successfully or not, re-throwing 228 // any exception from the passed in Promise. 229 async function clean_up_when_done(promise) { 230 var error; 231 try { 232 await promise; 233 } catch (e) { 234 error = e; 235 } 236 237 popup_windows.map(function (win) { win.close(); }); 238 239 try { 240 var cleanup_url = create_script_url(host.ORIGIN, uuid, host.ORIGIN, 'clean_up'); 241 var response = await fetch(cleanup_url, {credentials: 'omit', mode: 'cors'}); 242 assert_equals(await response.text(), 'cleanup complete', `Sever state cleanup failed`); 243 } catch (e) { 244 // Prefer error from the passed in Promise over errors from the fetch request to clean up server state. 245 error = error || e; 246 } 247 if (error) 248 throw error; 249 } 250 251 return clean_up_when_done( 252 make_test_function(test, 0)() 253 .then(make_test_function(test, 1))); 254 } 255 256 tests.forEach(function (test) { 257 promise_test( 258 function() { return run_test(test); }, 259 test.name); 260 }) 261 262 </script> 263 </body> 264 </html>