test_fetch_cors.js (49808B)
1 var path = "/tests/dom/base/test/"; 2 3 function isOpaqueResponse(response) { 4 return ( 5 response.type == "opaque" && 6 response.status === 0 && 7 response.statusText === "" 8 ); 9 } 10 11 function testModeSameOrigin() { 12 // Fetch spec Section 4, step 4, "request's mode is same-origin". 13 var req = new Request("https://example.net", { mode: "same-origin" }); 14 return fetch(req).then( 15 function (res) { 16 ok( 17 false, 18 "Attempting to fetch a resource from a different origin with mode same-origin should fail." 19 ); 20 }, 21 function (e) { 22 ok( 23 e instanceof TypeError, 24 "Attempting to fetch a resource from a different origin with mode same-origin should fail." 25 ); 26 } 27 ); 28 } 29 30 function testNoCorsCtor() { 31 // Request constructor Step 19.1 32 var simpleMethods = ["GET", "HEAD", "POST"]; 33 for (var i = 0; i < simpleMethods.length; ++i) { 34 var r = new Request("http://example.com", { 35 method: simpleMethods[i], 36 mode: "no-cors", 37 }); 38 ok( 39 true, 40 "no-cors Request with simple method " + simpleMethods[i] + " is allowed." 41 ); 42 } 43 44 var otherMethods = ["DELETE", "OPTIONS", "PUT"]; 45 for (var i = 0; i < otherMethods.length; ++i) { 46 try { 47 var r = new Request("http://example.com", { 48 method: otherMethods[i], 49 mode: "no-cors", 50 }); 51 ok( 52 false, 53 "no-cors Request with non-simple method " + 54 otherMethods[i] + 55 " is not allowed." 56 ); 57 } catch (e) { 58 ok( 59 true, 60 "no-cors Request with non-simple method " + 61 otherMethods[i] + 62 " is not allowed." 63 ); 64 } 65 } 66 67 // Request constructor Step 19.2, check guarded headers. 68 var r = new Request(".", { mode: "no-cors" }); 69 r.headers.append("Content-Type", "multipart/form-data"); 70 is( 71 r.headers.get("content-type"), 72 "multipart/form-data", 73 "Appending simple header should succeed" 74 ); 75 r.headers.append("custom", "value"); 76 ok(!r.headers.has("custom"), "Appending custom header should fail"); 77 r.headers.append("DNT", "value"); 78 ok(!r.headers.has("DNT"), "Appending forbidden header should fail"); 79 } 80 81 var corsServerPath = 82 "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"; 83 function testModeNoCors() { 84 // Fetch spec, section 4, step 4, response tainting should be set opaque, so 85 // that fetching leads to an opaque filtered response in step 8. 86 var r = new Request("https://example.net" + corsServerPath + "status=200", { 87 mode: "no-cors", 88 }); 89 return fetch(r).then( 90 function (res) { 91 ok( 92 isOpaqueResponse(res), 93 "no-cors Request fetch should result in opaque response" 94 ); 95 }, 96 function (e) { 97 ok(false, "no-cors Request fetch should not error"); 98 } 99 ); 100 } 101 102 function testSameOriginCredentials() { 103 var cookieStr = "type=chocolatechip"; 104 var tests = [ 105 { 106 // Initialize by setting a cookie. 107 pass: 1, 108 setCookie: cookieStr + "; Partitioned; Secure; SameSite=None", 109 withCred: "same-origin", 110 }, 111 { 112 // Default mode is "same-origin". 113 pass: 1, 114 cookie: cookieStr, 115 }, 116 { 117 pass: 1, 118 noCookie: 1, 119 withCred: "omit", 120 }, 121 { 122 pass: 1, 123 cookie: cookieStr, 124 withCred: "same-origin", 125 }, 126 { 127 pass: 1, 128 cookie: cookieStr, 129 withCred: "include", 130 }, 131 ]; 132 133 var finalPromiseResolve, finalPromiseReject; 134 var finalPromise = new Promise(function (res, rej) { 135 finalPromiseResolve = res; 136 finalPromiseReject = rej; 137 }); 138 139 function makeRequest(test) { 140 req = { 141 // Add a default query param just to make formatting the actual params 142 // easier. 143 url: corsServerPath + "a=b", 144 method: test.method, 145 headers: test.headers, 146 withCred: test.withCred, 147 }; 148 149 if (test.setCookie) { 150 req.url += "&setCookie=" + escape(test.setCookie); 151 } 152 if (test.cookie) { 153 req.url += "&cookie=" + escape(test.cookie); 154 } 155 if (test.noCookie) { 156 req.url += "&noCookie"; 157 } 158 159 return new Request(req.url, { 160 method: req.method, 161 headers: req.headers, 162 credentials: req.withCred, 163 }); 164 } 165 166 function testResponse(res, test) { 167 ok(test.pass, "Expected test to pass " + JSON.stringify(test)); 168 is(res.status, 200, "wrong status in test for " + JSON.stringify(test)); 169 is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test)); 170 return res.text().then(function (v) { 171 is( 172 v, 173 "<res>hello pass</res>\n", 174 "wrong text in test for " + JSON.stringify(test) 175 ); 176 }); 177 } 178 179 function runATest(tests, i) { 180 var test = tests[i]; 181 var request = makeRequest(test); 182 console.log(request.url); 183 fetch(request).then( 184 function (res) { 185 testResponse(res, test).then(function () { 186 if (i < tests.length - 1) { 187 runATest(tests, i + 1); 188 } else { 189 finalPromiseResolve(); 190 } 191 }); 192 }, 193 function (e) { 194 ok(!test.pass, "Expected test to fail " + JSON.stringify(test)); 195 ok(e instanceof TypeError, "Test should fail " + JSON.stringify(test)); 196 if (i < tests.length - 1) { 197 runATest(tests, i + 1); 198 } else { 199 finalPromiseResolve(); 200 } 201 } 202 ); 203 } 204 205 runATest(tests, 0); 206 return finalPromise; 207 } 208 209 function testModeCors() { 210 var tests = [ 211 // Plain request 212 { pass: 1, method: "GET", noAllowPreflight: 1 }, 213 214 // undefined username 215 { pass: 1, method: "GET", noAllowPreflight: 1, username: undefined }, 216 217 // undefined username and password 218 { 219 pass: 1, 220 method: "GET", 221 noAllowPreflight: 1, 222 username: undefined, 223 password: undefined, 224 }, 225 226 // nonempty username 227 { pass: 0, method: "GET", noAllowPreflight: 1, username: "user" }, 228 229 // nonempty password 230 { pass: 0, method: "GET", noAllowPreflight: 1, password: "password" }, 231 232 // Default allowed headers 233 { 234 pass: 1, 235 method: "GET", 236 headers: { 237 "Content-Type": "text/plain", 238 Accept: "foo/bar", 239 "Accept-Language": "sv-SE", 240 }, 241 noAllowPreflight: 1, 242 }, 243 244 { 245 pass: 0, 246 method: "GET", 247 headers: { 248 "Content-Type": "foo/bar", 249 Accept: "foo/bar", 250 "Accept-Language": "sv-SE", 251 }, 252 noAllowPreflight: 1, 253 }, 254 255 { 256 pass: 0, 257 method: "GET", 258 headers: { "Content-Type": "foo/bar, text/plain" }, 259 noAllowPreflight: 1, 260 }, 261 262 { 263 pass: 0, 264 method: "GET", 265 headers: { "Content-Type": "foo/bar, text/plain, garbage" }, 266 noAllowPreflight: 1, 267 }, 268 269 // Custom headers 270 { 271 pass: 1, 272 method: "GET", 273 headers: { "x-my-header": "myValue" }, 274 allowHeaders: "x-my-header", 275 }, 276 { 277 pass: 1, 278 method: "GET", 279 headers: { "x-my-header": "myValue" }, 280 allowHeaders: "X-My-Header", 281 }, 282 { 283 pass: 1, 284 method: "GET", 285 headers: { 286 "x-my-header": "myValue", 287 "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": 288 "secondValue", 289 }, 290 allowHeaders: 291 "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header", 292 }, 293 { 294 pass: 1, 295 method: "GET", 296 headers: { "x-my%-header": "myValue" }, 297 allowHeaders: "x-my%-header", 298 }, 299 { pass: 0, method: "GET", headers: { "x-my-header": "myValue" } }, 300 { pass: 0, method: "GET", headers: { "x-my-header": "" } }, 301 { 302 pass: 0, 303 method: "GET", 304 headers: { "x-my-header": "myValue" }, 305 allowHeaders: "", 306 }, 307 { 308 pass: 0, 309 method: "GET", 310 headers: { "x-my-header": "myValue" }, 311 allowHeaders: "y-my-header", 312 }, 313 { 314 pass: 0, 315 method: "GET", 316 headers: { "x-my-header": "myValue" }, 317 allowHeaders: "x-my-header y-my-header", 318 }, 319 { 320 pass: 0, 321 method: "GET", 322 headers: { "x-my-header": "myValue" }, 323 allowHeaders: "x-my-header, y-my-header z", 324 }, 325 { 326 pass: 0, 327 method: "GET", 328 headers: { "x-my-header": "myValue" }, 329 allowHeaders: "x-my-header, y-my-he(ader", 330 }, 331 { 332 pass: 0, 333 method: "GET", 334 headers: { myheader: "" }, 335 allowMethods: "myheader", 336 }, 337 { 338 pass: 1, 339 method: "GET", 340 headers: { "User-Agent": "myValue" }, 341 allowHeaders: "User-Agent", 342 }, 343 { pass: 0, method: "GET", headers: { "User-Agent": "myValue" } }, 344 345 // Multiple custom headers 346 { 347 pass: 1, 348 method: "GET", 349 headers: { 350 "x-my-header": "myValue", 351 "second-header": "secondValue", 352 "third-header": "thirdValue", 353 }, 354 allowHeaders: "x-my-header, second-header, third-header", 355 }, 356 { 357 pass: 1, 358 method: "GET", 359 headers: { 360 "x-my-header": "myValue", 361 "second-header": "secondValue", 362 "third-header": "thirdValue", 363 }, 364 allowHeaders: "x-my-header,second-header,third-header", 365 }, 366 { 367 pass: 1, 368 method: "GET", 369 headers: { 370 "x-my-header": "myValue", 371 "second-header": "secondValue", 372 "third-header": "thirdValue", 373 }, 374 allowHeaders: "x-my-header ,second-header ,third-header", 375 }, 376 { 377 pass: 1, 378 method: "GET", 379 headers: { 380 "x-my-header": "myValue", 381 "second-header": "secondValue", 382 "third-header": "thirdValue", 383 }, 384 allowHeaders: "x-my-header , second-header , third-header", 385 }, 386 { 387 pass: 1, 388 method: "GET", 389 headers: { "x-my-header": "myValue", "second-header": "secondValue" }, 390 allowHeaders: ", x-my-header, , ,, second-header, , ", 391 }, 392 { 393 pass: 1, 394 method: "GET", 395 headers: { "x-my-header": "myValue", "second-header": "secondValue" }, 396 allowHeaders: "x-my-header, second-header, unused-header", 397 }, 398 { 399 pass: 0, 400 method: "GET", 401 headers: { "x-my-header": "myValue", "y-my-header": "secondValue" }, 402 allowHeaders: "x-my-header", 403 }, 404 { 405 pass: 0, 406 method: "GET", 407 headers: { "x-my-header": "", "y-my-header": "" }, 408 allowHeaders: "x-my-header", 409 }, 410 411 // HEAD requests 412 { pass: 1, method: "HEAD", noAllowPreflight: 1 }, 413 414 // HEAD with safe headers 415 { 416 pass: 1, 417 method: "HEAD", 418 headers: { 419 "Content-Type": "text/plain", 420 Accept: "foo/bar", 421 "Accept-Language": "sv-SE", 422 }, 423 noAllowPreflight: 1, 424 }, 425 { 426 pass: 0, 427 method: "HEAD", 428 headers: { 429 "Content-Type": "foo/bar", 430 Accept: "foo/bar", 431 "Accept-Language": "sv-SE", 432 }, 433 noAllowPreflight: 1, 434 }, 435 { 436 pass: 0, 437 method: "HEAD", 438 headers: { "Content-Type": "foo/bar, text/plain" }, 439 noAllowPreflight: 1, 440 }, 441 { 442 pass: 0, 443 method: "HEAD", 444 headers: { "Content-Type": "foo/bar, text/plain, garbage" }, 445 noAllowPreflight: 1, 446 }, 447 448 // HEAD with custom headers 449 { 450 pass: 1, 451 method: "HEAD", 452 headers: { "x-my-header": "myValue" }, 453 allowHeaders: "x-my-header", 454 }, 455 { pass: 0, method: "HEAD", headers: { "x-my-header": "myValue" } }, 456 { 457 pass: 0, 458 method: "HEAD", 459 headers: { "x-my-header": "myValue" }, 460 allowHeaders: "", 461 }, 462 { 463 pass: 0, 464 method: "HEAD", 465 headers: { "x-my-header": "myValue" }, 466 allowHeaders: "y-my-header", 467 }, 468 { 469 pass: 0, 470 method: "HEAD", 471 headers: { "x-my-header": "myValue" }, 472 allowHeaders: "x-my-header y-my-header", 473 }, 474 475 // POST tests 476 { pass: 1, method: "POST", body: "hi there", noAllowPreflight: 1 }, 477 { pass: 1, method: "POST" }, 478 { pass: 1, method: "POST", noAllowPreflight: 1 }, 479 480 // POST with standard headers 481 { 482 pass: 1, 483 method: "POST", 484 body: "hi there", 485 headers: { "Content-Type": "text/plain" }, 486 noAllowPreflight: 1, 487 }, 488 { 489 pass: 1, 490 method: "POST", 491 body: "hi there", 492 headers: { "Content-Type": "multipart/form-data" }, 493 noAllowPreflight: 1, 494 }, 495 { 496 pass: 1, 497 method: "POST", 498 body: "hi there", 499 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 500 noAllowPreflight: 1, 501 }, 502 { 503 pass: 0, 504 method: "POST", 505 body: "hi there", 506 headers: { "Content-Type": "foo/bar" }, 507 }, 508 { pass: 0, method: "POST", headers: { "Content-Type": "foo/bar" } }, 509 { 510 pass: 1, 511 method: "POST", 512 body: "hi there", 513 headers: { 514 "Content-Type": "text/plain", 515 Accept: "foo/bar", 516 "Accept-Language": "sv-SE", 517 }, 518 noAllowPreflight: 1, 519 }, 520 { 521 pass: 0, 522 method: "POST", 523 body: "hi there", 524 headers: { "Content-Type": "foo/bar, text/plain" }, 525 noAllowPreflight: 1, 526 }, 527 { 528 pass: 0, 529 method: "POST", 530 body: "hi there", 531 headers: { "Content-Type": "foo/bar, text/plain, garbage" }, 532 noAllowPreflight: 1, 533 }, 534 535 // POST with custom headers 536 { 537 pass: 1, 538 method: "POST", 539 body: "hi there", 540 headers: { 541 Accept: "foo/bar", 542 "Accept-Language": "sv-SE", 543 "x-my-header": "myValue", 544 }, 545 allowHeaders: "x-my-header", 546 }, 547 { 548 pass: 1, 549 method: "POST", 550 headers: { "Content-Type": "text/plain", "x-my-header": "myValue" }, 551 allowHeaders: "x-my-header", 552 }, 553 { 554 pass: 1, 555 method: "POST", 556 body: "hi there", 557 headers: { "Content-Type": "text/plain", "x-my-header": "myValue" }, 558 allowHeaders: "x-my-header", 559 }, 560 { 561 pass: 1, 562 method: "POST", 563 body: "hi there", 564 headers: { "Content-Type": "foo/bar", "x-my-header": "myValue" }, 565 allowHeaders: "x-my-header, content-type", 566 }, 567 { 568 pass: 0, 569 method: "POST", 570 body: "hi there", 571 headers: { "Content-Type": "foo/bar" }, 572 noAllowPreflight: 1, 573 }, 574 { 575 pass: 0, 576 method: "POST", 577 body: "hi there", 578 headers: { "Content-Type": "foo/bar", "x-my-header": "myValue" }, 579 allowHeaders: "x-my-header", 580 }, 581 { 582 pass: 1, 583 method: "POST", 584 headers: { "x-my-header": "myValue" }, 585 allowHeaders: "x-my-header", 586 }, 587 { 588 pass: 1, 589 method: "POST", 590 body: "hi there", 591 headers: { "x-my-header": "myValue" }, 592 allowHeaders: "x-my-header, $_%", 593 }, 594 595 // Other methods 596 { pass: 1, method: "DELETE", allowMethods: "DELETE" }, 597 { pass: 0, method: "DELETE", allowHeaders: "DELETE" }, 598 { pass: 0, method: "DELETE" }, 599 { pass: 0, method: "DELETE", allowMethods: "" }, 600 { pass: 1, method: "DELETE", allowMethods: "POST, PUT, DELETE" }, 601 { pass: 1, method: "DELETE", allowMethods: "POST, DELETE, PUT" }, 602 { pass: 1, method: "DELETE", allowMethods: "DELETE, POST, PUT" }, 603 { pass: 1, method: "DELETE", allowMethods: "POST ,PUT ,DELETE" }, 604 { pass: 1, method: "DELETE", allowMethods: "POST,PUT,DELETE" }, 605 { pass: 1, method: "DELETE", allowMethods: "POST , PUT , DELETE" }, 606 { 607 pass: 1, 608 method: "DELETE", 609 allowMethods: " ,, PUT ,, , , DELETE , ,", 610 }, 611 { pass: 0, method: "DELETE", allowMethods: "PUT" }, 612 { pass: 0, method: "DELETE", allowMethods: "DELETEZ" }, 613 { pass: 0, method: "DELETE", allowMethods: "DELETE PUT" }, 614 { pass: 0, method: "DELETE", allowMethods: "DELETE, PUT Z" }, 615 { pass: 0, method: "DELETE", allowMethods: "DELETE, PU(T" }, 616 { pass: 0, method: "DELETE", allowMethods: "PUT DELETE" }, 617 { pass: 0, method: "DELETE", allowMethods: "PUT Z, DELETE" }, 618 { pass: 0, method: "DELETE", allowMethods: "PU(T, DELETE" }, 619 { pass: 0, method: "PUT", allowMethods: "put" }, 620 621 // Status messages 622 { 623 pass: 1, 624 method: "GET", 625 noAllowPreflight: 1, 626 status: 404, 627 statusMessage: "nothin' here", 628 }, 629 { 630 pass: 1, 631 method: "GET", 632 noAllowPreflight: 1, 633 status: 401, 634 statusMessage: "no can do", 635 }, 636 { 637 pass: 1, 638 method: "POST", 639 body: "hi there", 640 headers: { "Content-Type": "foo/bar" }, 641 allowHeaders: "content-type", 642 status: 500, 643 statusMessage: "server boo", 644 }, 645 { 646 pass: 1, 647 method: "GET", 648 noAllowPreflight: 1, 649 status: 200, 650 statusMessage: "Yes!!", 651 }, 652 { 653 pass: 0, 654 method: "GET", 655 headers: { "x-my-header": "header value" }, 656 allowHeaders: "x-my-header", 657 preflightStatus: 400, 658 }, 659 { 660 pass: 1, 661 method: "GET", 662 headers: { "x-my-header": "header value" }, 663 allowHeaders: "x-my-header", 664 preflightStatus: 200, 665 }, 666 { 667 pass: 1, 668 method: "GET", 669 headers: { "x-my-header": "header value" }, 670 allowHeaders: "x-my-header", 671 preflightStatus: 204, 672 }, 673 674 // exposed headers 675 { 676 pass: 1, 677 method: "GET", 678 responseHeaders: { "x-my-header": "x header" }, 679 exposeHeaders: "x-my-header", 680 expectedResponseHeaders: ["x-my-header"], 681 }, 682 { 683 pass: 0, 684 method: "GET", 685 origin: "http://invalid", 686 responseHeaders: { "x-my-header": "x header" }, 687 exposeHeaders: "x-my-header", 688 expectedResponseHeaders: [], 689 }, 690 { 691 pass: 1, 692 method: "GET", 693 responseHeaders: { "x-my-header": "x header" }, 694 expectedResponseHeaders: [], 695 }, 696 { 697 pass: 1, 698 method: "GET", 699 responseHeaders: { "x-my-header": "x header" }, 700 exposeHeaders: "x-my-header y", 701 expectedResponseHeaders: [], 702 }, 703 { 704 pass: 1, 705 method: "GET", 706 responseHeaders: { "x-my-header": "x header" }, 707 exposeHeaders: "y x-my-header", 708 expectedResponseHeaders: [], 709 }, 710 { 711 pass: 1, 712 method: "GET", 713 responseHeaders: { "x-my-header": "x header" }, 714 exposeHeaders: "x-my-header, y-my-header z", 715 expectedResponseHeaders: [], 716 }, 717 { 718 pass: 1, 719 method: "GET", 720 responseHeaders: { "x-my-header": "x header" }, 721 exposeHeaders: "x-my-header, y-my-hea(er", 722 expectedResponseHeaders: [], 723 }, 724 { 725 pass: 1, 726 method: "GET", 727 responseHeaders: { "x-my-header": "x header", "y-my-header": "y header" }, 728 exposeHeaders: " , ,,y-my-header,z-my-header, ", 729 expectedResponseHeaders: ["y-my-header"], 730 }, 731 { 732 pass: 1, 733 method: "GET", 734 responseHeaders: { 735 "Cache-Control": "cacheControl header", 736 "Content-Language": "contentLanguage header", 737 Expires: "expires header", 738 "Last-Modified": "lastModified header", 739 Pragma: "pragma header", 740 Unexpected: "unexpected header", 741 }, 742 expectedResponseHeaders: [ 743 "Cache-Control", 744 "Content-Language", 745 "Content-Type", 746 "Expires", 747 "Last-Modified", 748 "Pragma", 749 ], 750 }, 751 // Check that sending a body in the OPTIONS response works 752 { 753 pass: 1, 754 method: "DELETE", 755 allowMethods: "DELETE", 756 preflightBody: "I'm a preflight response body", 757 }, 758 ]; 759 760 var origin = self.location.origin; 761 var baseURL = 762 origin == "https://example.com" 763 ? "https://example.org" 764 : "https://example.com"; 765 baseURL += corsServerPath; 766 767 var fetches = []; 768 for (test of tests) { 769 var req = { 770 url: baseURL + "allowOrigin=" + escape(test.origin || origin), 771 method: test.method, 772 headers: test.headers, 773 uploadProgress: test.uploadProgress, 774 body: test.body, 775 responseHeaders: test.responseHeaders, 776 }; 777 778 if (test.pass) { 779 req.url += "&origin=" + escape(origin) + "&requestMethod=" + test.method; 780 } 781 782 if ("username" in test) { 783 var u = new URL(req.url); 784 u.username = test.username || ""; 785 req.url = u.href; 786 } 787 788 if ("password" in test) { 789 var u = new URL(req.url); 790 u.password = test.password || ""; 791 req.url = u.href; 792 } 793 794 if (test.noAllowPreflight) { 795 req.url += "&noAllowPreflight"; 796 } 797 798 if (test.pass && "headers" in test) { 799 function isUnsafeHeader(name) { 800 lName = name.toLowerCase(); 801 return ( 802 lName != "accept" && 803 lName != "accept-language" && 804 (lName != "content-type" || 805 ![ 806 "text/plain", 807 "multipart/form-data", 808 "application/x-www-form-urlencoded", 809 ].includes(test.headers[name].toLowerCase())) 810 ); 811 } 812 req.url += "&headers=" + escape(JSON.stringify(test.headers)); 813 reqHeaders = escape( 814 Object.keys(test.headers) 815 .filter(isUnsafeHeader) 816 .map(s => s.toLowerCase()) 817 .sort() 818 .join(",") 819 ); 820 req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : ""; 821 } 822 if ("allowHeaders" in test) { 823 req.url += "&allowHeaders=" + escape(test.allowHeaders); 824 } 825 if ("allowMethods" in test) { 826 req.url += "&allowMethods=" + escape(test.allowMethods); 827 } 828 if (test.body) { 829 req.url += "&body=" + escape(test.body); 830 } 831 if (test.status) { 832 req.url += "&status=" + test.status; 833 req.url += "&statusMessage=" + escape(test.statusMessage); 834 } 835 if (test.preflightStatus) { 836 req.url += "&preflightStatus=" + test.preflightStatus; 837 } 838 if (test.responseHeaders) { 839 req.url += 840 "&responseHeaders=" + escape(JSON.stringify(test.responseHeaders)); 841 } 842 if (test.exposeHeaders) { 843 req.url += "&exposeHeaders=" + escape(test.exposeHeaders); 844 } 845 if (test.preflightBody) { 846 req.url += "&preflightBody=" + escape(test.preflightBody); 847 } 848 849 fetches.push( 850 (function (test) { 851 return new Promise(function (resolve) { 852 resolve( 853 new Request(req.url, { 854 method: req.method, 855 mode: "cors", 856 headers: req.headers, 857 body: req.body, 858 }) 859 ); 860 }) 861 .then(function (request) { 862 return fetch(request); 863 }) 864 .then(function (res) { 865 ok(test.pass, "Expected test to pass for " + JSON.stringify(test)); 866 if (test.status) { 867 is( 868 res.status, 869 test.status, 870 "wrong status in test for " + JSON.stringify(test) 871 ); 872 is( 873 res.statusText, 874 test.statusMessage, 875 "wrong status text for " + JSON.stringify(test) 876 ); 877 } else { 878 is( 879 res.status, 880 200, 881 "wrong status in test for " + JSON.stringify(test) 882 ); 883 is( 884 res.statusText, 885 "OK", 886 "wrong status text for " + JSON.stringify(test) 887 ); 888 } 889 if (test.responseHeaders) { 890 for (header in test.responseHeaders) { 891 if (!test.expectedResponseHeaders.includes(header)) { 892 is( 893 res.headers.has(header), 894 false, 895 "|Headers.has()|wrong response header (" + 896 header + 897 ") in test for " + 898 JSON.stringify(test) 899 ); 900 } else { 901 is( 902 res.headers.get(header), 903 test.responseHeaders[header], 904 "|Headers.get()|wrong response header (" + 905 header + 906 ") in test for " + 907 JSON.stringify(test) 908 ); 909 } 910 } 911 } 912 913 return res.text(); 914 }) 915 .then(function (v) { 916 if (test.method !== "HEAD") { 917 is( 918 v, 919 "<res>hello pass</res>\n", 920 "wrong responseText in test for " + JSON.stringify(test) 921 ); 922 } else { 923 is( 924 v, 925 "", 926 "wrong responseText in HEAD test for " + JSON.stringify(test) 927 ); 928 } 929 }) 930 .catch(function (e) { 931 ok(!test.pass, "Expected test failure for " + JSON.stringify(test)); 932 ok( 933 e instanceof TypeError, 934 "Exception should be TypeError for " + JSON.stringify(test) 935 ); 936 }); 937 })(test) 938 ); 939 } 940 941 return Promise.all(fetches); 942 } 943 944 function testCrossOriginCredentials() { 945 var origin = self.location.origin; 946 947 var tests = [ 948 { pass: 1, method: "GET", withCred: "include", allowCred: 1 }, 949 { pass: 0, method: "GET", withCred: "include", allowCred: 0 }, 950 { pass: 0, method: "GET", withCred: "include", allowCred: 1, origin: "*" }, 951 { pass: 1, method: "GET", withCred: "omit", allowCred: 1, origin: "*" }, 952 { 953 pass: 1, 954 method: "GET", 955 setCookie: "a=1; Partitioned; Secure; SameSite=None", 956 withCred: "include", 957 allowCred: 1, 958 }, 959 { 960 pass: 1, 961 method: "GET", 962 cookie: "a=1", 963 withCred: "include", 964 allowCred: 1, 965 }, 966 { pass: 1, method: "GET", noCookie: 1, withCred: "omit", allowCred: 1 }, 967 { pass: 0, method: "GET", noCookie: 1, withCred: "include", allowCred: 1 }, 968 { 969 pass: 1, 970 method: "GET", 971 setCookie: "a=2; Partitioned; Secure; SameSite=None", 972 withCred: "omit", 973 allowCred: 1, 974 }, 975 { 976 pass: 1, 977 method: "GET", 978 cookie: "a=1", 979 withCred: "include", 980 allowCred: 1, 981 }, 982 { 983 pass: 1, 984 method: "GET", 985 setCookie: "a=2; Partitioned; Secure; SameSite=None", 986 withCred: "include", 987 allowCred: 1, 988 }, 989 { 990 pass: 1, 991 method: "GET", 992 cookie: "a=2", 993 withCred: "include", 994 allowCred: 1, 995 }, 996 { 997 // When credentials mode is same-origin, but mode is cors, no 998 // cookie should be sent cross origin. 999 pass: 0, 1000 method: "GET", 1001 cookie: "a=2", 1002 withCred: "same-origin", 1003 allowCred: 1, 1004 }, 1005 { 1006 // When credentials mode is same-origin, but mode is cors, no 1007 // cookie should be sent cross origin. This test checks the same 1008 // thing as above, but uses the noCookie check on the server 1009 // instead, and expects a valid response. 1010 pass: 1, 1011 method: "GET", 1012 noCookie: 1, 1013 withCred: "same-origin", 1014 }, 1015 { 1016 // Initialize by setting a cookies for same- and cross- origins. 1017 pass: 1, 1018 hops: [ 1019 { 1020 server: origin, 1021 setCookie: escape("a=1; Partitioned; Secure; SameSite=None"), 1022 }, 1023 { 1024 server: "https://example.net", 1025 allowOrigin: origin, 1026 allowCred: 1, 1027 setCookie: escape("a=2; Partitioned; Secure; SameSite=None"), 1028 }, 1029 ], 1030 withCred: "include", 1031 }, 1032 { 1033 pass: 1, 1034 method: "GET", 1035 hops: [ 1036 { server: origin, cookie: escape("a=1") }, 1037 { server: origin, cookie: escape("a=1") }, 1038 { server: "https://example.net", allowOrigin: origin, noCookie: 1 }, 1039 ], 1040 withCred: "same-origin", 1041 }, 1042 { 1043 pass: 1, 1044 method: "GET", 1045 hops: [ 1046 { server: origin, cookie: escape("a=1") }, 1047 { server: origin, cookie: escape("a=1") }, 1048 { 1049 server: "https://example.net", 1050 allowOrigin: origin, 1051 allowCred: 1, 1052 cookie: escape("a=2"), 1053 }, 1054 ], 1055 withCred: "include", 1056 }, 1057 { 1058 pass: 1, 1059 method: "GET", 1060 hops: [ 1061 { server: origin, cookie: escape("a=1") }, 1062 { server: origin, cookie: escape("a=1") }, 1063 { server: "https://example.net", allowOrigin: "*", noCookie: 1 }, 1064 ], 1065 withCred: "same-origin", 1066 }, 1067 { 1068 pass: 0, 1069 method: "GET", 1070 hops: [ 1071 { server: origin, cookie: escape("a=1") }, 1072 { server: origin, cookie: escape("a=1") }, 1073 { 1074 server: "https://example.net", 1075 allowOrigin: "*", 1076 allowCred: 1, 1077 cookie: escape("a=2"), 1078 }, 1079 ], 1080 withCred: "include", 1081 }, 1082 // fails because allow-credentials CORS header is not set by server 1083 { 1084 pass: 0, 1085 method: "GET", 1086 hops: [ 1087 { server: origin, cookie: escape("a=1") }, 1088 { server: origin, cookie: escape("a=1") }, 1089 { 1090 server: "https://example.net", 1091 allowOrigin: origin, 1092 cookie: escape("a=2"), 1093 }, 1094 ], 1095 withCred: "include", 1096 }, 1097 { 1098 pass: 1, 1099 method: "GET", 1100 hops: [ 1101 { server: origin, noCookie: 1 }, 1102 { server: origin, noCookie: 1 }, 1103 { server: "https://example.net", allowOrigin: origin, noCookie: 1 }, 1104 ], 1105 withCred: "omit", 1106 }, 1107 ]; 1108 1109 // Xorigin with https has .org origin, default .com 1110 var baseURL = 1111 origin == "https://example.com" 1112 ? "https://example.org" 1113 : "https://example.com"; 1114 baseURL += corsServerPath; 1115 1116 var finalPromiseResolve, finalPromiseReject; 1117 var finalPromise = new Promise(function (res, rej) { 1118 finalPromiseResolve = res; 1119 finalPromiseReject = rej; 1120 }); 1121 1122 function makeRequest(test) { 1123 var url; 1124 if (test.hops) { 1125 url = 1126 test.hops[0].server + 1127 corsServerPath + 1128 "hop=1&hops=" + 1129 escape(JSON.stringify(test.hops)); 1130 } else { 1131 url = baseURL + "allowOrigin=" + escape(test.origin || origin); 1132 } 1133 req = { 1134 url, 1135 method: test.method, 1136 headers: test.headers, 1137 withCred: test.withCred, 1138 }; 1139 1140 if (test.allowCred) { 1141 req.url += "&allowCred"; 1142 } 1143 1144 if (test.setCookie) { 1145 req.url += "&setCookie=" + escape(test.setCookie); 1146 } 1147 if (test.cookie) { 1148 req.url += "&cookie=" + escape(test.cookie); 1149 } 1150 if (test.noCookie) { 1151 req.url += "&noCookie"; 1152 } 1153 1154 if ("allowHeaders" in test) { 1155 req.url += "&allowHeaders=" + escape(test.allowHeaders); 1156 } 1157 if ("allowMethods" in test) { 1158 req.url += "&allowMethods=" + escape(test.allowMethods); 1159 } 1160 1161 return new Request(req.url, { 1162 method: req.method, 1163 headers: req.headers, 1164 credentials: req.withCred, 1165 }); 1166 } 1167 1168 function testResponse(res, test) { 1169 ok(test.pass, "Expected test to pass for " + JSON.stringify(test)); 1170 is(res.status, 200, "wrong status in test for " + JSON.stringify(test)); 1171 is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test)); 1172 return res.text().then(function (v) { 1173 is( 1174 v, 1175 "<res>hello pass</res>\n", 1176 "wrong text in test for " + JSON.stringify(test) 1177 ); 1178 }); 1179 } 1180 1181 function runATest(tests, i) { 1182 var test = tests[i]; 1183 var request = makeRequest(test); 1184 fetch(request).then( 1185 function (res) { 1186 testResponse(res, test).then(function () { 1187 if (i < tests.length - 1) { 1188 runATest(tests, i + 1); 1189 } else { 1190 finalPromiseResolve(); 1191 } 1192 }); 1193 }, 1194 function (e) { 1195 ok(!test.pass, "Expected test failure for " + JSON.stringify(test)); 1196 ok( 1197 e instanceof TypeError, 1198 "Exception should be TypeError for " + JSON.stringify(test) 1199 ); 1200 if (i < tests.length - 1) { 1201 runATest(tests, i + 1); 1202 } else { 1203 finalPromiseResolve(); 1204 } 1205 } 1206 ); 1207 } 1208 1209 runATest(tests, 0); 1210 return finalPromise; 1211 } 1212 1213 function testModeNoCorsCredentials() { 1214 var cookieStr = "type=chocolatechip"; 1215 var tests = [ 1216 { 1217 // Initialize by setting a cookie. 1218 pass: 1, 1219 setCookie: cookieStr + "; Partitioned; Secure; SameSite=None", 1220 withCred: "include", 1221 }, 1222 { 1223 pass: 1, 1224 noCookie: 1, 1225 withCred: "omit", 1226 }, 1227 { 1228 pass: 1, 1229 noCookie: 1, 1230 withCred: "same-origin", 1231 }, 1232 { 1233 pass: 1, 1234 cookie: cookieStr, 1235 withCred: "include", 1236 }, 1237 { 1238 pass: 1, 1239 cookie: cookieStr, 1240 withCred: "omit", 1241 status: 500, 1242 }, 1243 { 1244 pass: 1, 1245 cookie: cookieStr, 1246 withCred: "same-origin", 1247 status: 500, 1248 }, 1249 { 1250 pass: 1, 1251 noCookie: 1, 1252 withCred: "include", 1253 status: 500, 1254 }, 1255 ]; 1256 1257 var finalPromiseResolve, finalPromiseReject; 1258 var finalPromise = new Promise(function (res, rej) { 1259 finalPromiseResolve = res; 1260 finalPromiseReject = rej; 1261 }); 1262 1263 function makeRequest(test) { 1264 req = { 1265 url: "https://example.net" + corsServerPath + "a+b", 1266 withCred: test.withCred, 1267 }; 1268 1269 if (test.setCookie) { 1270 req.url += "&setCookie=" + escape(test.setCookie); 1271 } 1272 if (test.cookie) { 1273 req.url += "&cookie=" + escape(test.cookie); 1274 } 1275 if (test.noCookie) { 1276 req.url += "&noCookie"; 1277 } 1278 1279 return new Request(req.url, { 1280 method: "GET", 1281 mode: "no-cors", 1282 credentials: req.withCred, 1283 }); 1284 } 1285 1286 function testResponse(res, test) { 1287 is(res.type, "opaque", "wrong response type for " + JSON.stringify(test)); 1288 1289 // Get unfiltered response 1290 var chromeResponse = SpecialPowers.wrap(res); 1291 var unfiltered = chromeResponse.cloneUnfiltered(); 1292 1293 var status = test.status ? test.status : 200; 1294 is( 1295 unfiltered.status, 1296 status, 1297 "wrong status in test for " + JSON.stringify(test) 1298 ); 1299 1300 return unfiltered.text().then(function (v) { 1301 if (test.status === 200) { 1302 const expected = 1303 SpecialPowers.getIntPref( 1304 "browser.opaqueResponseBlocking.filterFetchResponse" 1305 ) > 0 1306 ? "" 1307 : "<res>hello pass</res>\n"; 1308 is(v, expected, "wrong text in test for " + JSON.stringify(test)); 1309 } 1310 }); 1311 } 1312 1313 function runATest(tests, i) { 1314 if (typeof SpecialPowers !== "object") { 1315 finalPromiseResolve(); 1316 return; 1317 } 1318 1319 var test = tests[i]; 1320 var request = makeRequest(test); 1321 fetch(request).then( 1322 function (res) { 1323 ok(test.pass, "Expected test to pass " + JSON.stringify(test)); 1324 testResponse(res, test).then(function () { 1325 if (i < tests.length - 1) { 1326 runATest(tests, i + 1); 1327 } else { 1328 finalPromiseResolve(); 1329 } 1330 }); 1331 }, 1332 function (e) { 1333 ok(!test.pass, "Expected test to fail " + JSON.stringify(test)); 1334 ok(e instanceof TypeError, "Test should fail " + JSON.stringify(test)); 1335 if (i < tests.length - 1) { 1336 runATest(tests, i + 1); 1337 } else { 1338 finalPromiseResolve(); 1339 } 1340 } 1341 ); 1342 } 1343 1344 runATest(tests, 0); 1345 return finalPromise; 1346 } 1347 1348 function testCORSRedirects() { 1349 var origin = self.location.origin; 1350 1351 var host = self.location.hostname; 1352 var protocol = self.location.protocol; 1353 var originSubSub1 = protocol + "//sub1.test1." + host; 1354 var originSubSub2 = protocol + "//sub2.test2." + host; 1355 var originSub = protocol + "//test3." + host; 1356 1357 var foreignHost = host === "example.com" ? "example.org" : "example.com"; 1358 var foreignSub1 = protocol + "//test1." + foreignHost; 1359 var foreignSub2 = protocol + "//test2." + foreignHost; 1360 1361 var tests = [ 1362 { 1363 pass: 1, 1364 method: "GET", 1365 hops: [{ server: "https://example.net", allowOrigin: origin }], 1366 }, 1367 { 1368 pass: 0, 1369 method: "GET", 1370 hops: [ 1371 { server: "https://example.net", allowOrigin: origin }, 1372 { server: origin, allowOrigin: origin }, 1373 ], 1374 }, 1375 { 1376 pass: 1, 1377 method: "GET", 1378 hops: [ 1379 { server: "https://example.net", allowOrigin: origin }, 1380 { server: origin, allowOrigin: "*" }, 1381 ], 1382 }, 1383 { 1384 pass: 0, 1385 method: "GET", 1386 hops: [ 1387 { server: "https://example.net", allowOrigin: origin }, 1388 { server: origin }, 1389 ], 1390 }, 1391 { 1392 pass: 1, 1393 method: "GET", 1394 hops: [ 1395 { server: origin }, 1396 { server: origin }, 1397 { server: "https://example.net", allowOrigin: origin }, 1398 ], 1399 }, 1400 { 1401 pass: 0, 1402 method: "GET", 1403 hops: [ 1404 { server: origin }, 1405 { server: origin }, 1406 { server: "https://example.net", allowOrigin: origin }, 1407 { server: origin }, 1408 ], 1409 }, 1410 { 1411 pass: 0, 1412 method: "GET", 1413 hops: [ 1414 { server: "https://example.net", allowOrigin: origin }, 1415 { server: originSub, allowOrigin: origin }, 1416 { server: originSubSub1, allowOrigin: origin }, 1417 { server: originSubSub2, allowOrigin: origin }, 1418 ], 1419 }, 1420 { 1421 pass: 0, 1422 method: "GET", 1423 hops: [ 1424 { server: "https://example.net", allowOrigin: origin }, 1425 { server: originSub, allowOrigin: origin }, 1426 { server: originSubSub1, allowOrigin: "*" }, 1427 { server: originSubSub2, allowOrigin: "*" }, 1428 ], 1429 }, 1430 { 1431 pass: 1, 1432 method: "GET", 1433 hops: [ 1434 { server: "https://example.net", allowOrigin: origin }, 1435 { server: originSub, allowOrigin: "*" }, 1436 { server: originSubSub1, allowOrigin: "*" }, 1437 { server: originSubSub2, allowOrigin: "*" }, 1438 ], 1439 }, 1440 { 1441 pass: 0, 1442 method: "GET", 1443 hops: [ 1444 { server: "https://example.net", allowOrigin: origin }, 1445 { server: originSub, allowOrigin: origin }, 1446 { server: originSubSub1, allowOrigin: "x" }, 1447 { server: originSubSub2, allowOrigin: origin }, 1448 ], 1449 }, 1450 { 1451 pass: 0, 1452 method: "GET", 1453 hops: [ 1454 { server: "https://example.net", allowOrigin: origin }, 1455 { server: originSub, allowOrigin: origin }, 1456 { server: originSubSub1, allowOrigin: "*" }, 1457 { server: originSubSub2, allowOrigin: origin }, 1458 ], 1459 }, 1460 { 1461 pass: 0, 1462 method: "GET", 1463 hops: [ 1464 { server: "https://example.net", allowOrigin: origin }, 1465 { server: originSub, allowOrigin: origin }, 1466 { server: originSubSub1, allowOrigin: "*" }, 1467 { server: originSubSub2 }, 1468 ], 1469 }, 1470 { 1471 pass: 1, 1472 method: "POST", 1473 body: "hi there", 1474 headers: { "Content-Type": "text/plain" }, 1475 hops: [ 1476 { server: origin }, 1477 { server: "https://example.net", allowOrigin: origin }, 1478 ], 1479 }, 1480 { 1481 pass: 1, 1482 method: "POST", 1483 body: "hi there", 1484 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1485 hops: [ 1486 { server: origin }, 1487 { 1488 server: "https://example.net", 1489 allowOrigin: origin, 1490 allowHeaders: "my-header", 1491 }, 1492 ], 1493 }, 1494 { 1495 pass: 0, 1496 method: "POST", 1497 body: "hi there", 1498 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1499 hops: [ 1500 { server: origin }, 1501 { 1502 server: "https://example.net", 1503 allowOrigin: origin, 1504 allowHeaders: "my-header", 1505 noAllowPreflight: 1, 1506 }, 1507 ], 1508 }, 1509 { 1510 pass: 0, 1511 method: "POST", 1512 body: "hi there", 1513 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1514 hops: [ 1515 { server: origin }, 1516 { 1517 server: foreignSub1, 1518 allowOrigin: origin, 1519 allowHeaders: "my-header", 1520 }, 1521 { 1522 server: foreignSub2, 1523 allowOrigin: origin, 1524 allowHeaders: "my-header", 1525 }, 1526 ], 1527 }, 1528 { 1529 pass: 1, 1530 method: "DELETE", 1531 hops: [ 1532 { server: origin }, 1533 { 1534 server: "https://example.net", 1535 allowOrigin: origin, 1536 allowMethods: "DELETE", 1537 }, 1538 ], 1539 }, 1540 { 1541 pass: 0, 1542 method: "DELETE", 1543 hops: [ 1544 { server: origin }, 1545 { 1546 server: "https://example.net", 1547 allowOrigin: origin, 1548 allowMethods: "DELETE", 1549 noAllowPreflight: 1, 1550 }, 1551 ], 1552 }, 1553 { 1554 pass: 0, 1555 method: "DELETE", 1556 hops: [ 1557 { server: origin }, 1558 { 1559 server: foreignSub1, 1560 allowOrigin: origin, 1561 allowMethods: "DELETE", 1562 }, 1563 { 1564 server: foreignSub2, 1565 allowOrigin: origin, 1566 allowMethods: "DELETE", 1567 }, 1568 ], 1569 }, 1570 { 1571 pass: 0, 1572 method: "POST", 1573 body: "hi there", 1574 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1575 hops: [ 1576 { server: "https://example.net", allowOrigin: origin }, 1577 { server: originSubSub1, allowOrigin: origin }, 1578 ], 1579 }, 1580 { 1581 pass: 0, 1582 method: "DELETE", 1583 hops: [ 1584 { 1585 server: "https://example.net", 1586 allowOrigin: origin, 1587 allowMethods: "DELETE", 1588 }, 1589 { 1590 server: originSubSub1, 1591 allowOrigin: origin, 1592 allowMethods: "DELETE", 1593 }, 1594 ], 1595 }, 1596 { 1597 pass: 0, 1598 method: "POST", 1599 body: "hi there", 1600 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1601 hops: [ 1602 { server: "https://example.net" }, 1603 { 1604 server: originSubSub1, 1605 allowOrigin: origin, 1606 allowHeaders: "my-header", 1607 }, 1608 ], 1609 }, 1610 { 1611 pass: 1, 1612 method: "POST", 1613 body: "hi there", 1614 headers: { "Content-Type": "text/plain" }, 1615 hops: [ 1616 { server: origin }, 1617 { server: "https://example.net", allowOrigin: origin }, 1618 ], 1619 }, 1620 { 1621 pass: 0, 1622 method: "POST", 1623 body: "hi there", 1624 headers: { "Content-Type": "text/plain", "my-header": "myValue" }, 1625 hops: [ 1626 { 1627 server: "https://example.net", 1628 allowOrigin: origin, 1629 allowHeaders: "my-header", 1630 }, 1631 { 1632 server: origin, 1633 allowOrigin: origin, 1634 allowHeaders: "my-header", 1635 }, 1636 ], 1637 }, 1638 ]; 1639 1640 var fetches = []; 1641 for (test of tests) { 1642 req = { 1643 url: 1644 test.hops[0].server + 1645 corsServerPath + 1646 "hop=1&hops=" + 1647 escape(JSON.stringify(test.hops)), 1648 method: test.method, 1649 headers: test.headers, 1650 body: test.body, 1651 }; 1652 1653 if (test.headers) { 1654 req.url += "&headers=" + escape(JSON.stringify(test.headers)); 1655 } 1656 1657 if (test.pass) { 1658 if (test.body) { 1659 req.url += "&body=" + escape(test.body); 1660 } 1661 } 1662 1663 var request = new Request(req.url, { 1664 method: req.method, 1665 headers: req.headers, 1666 body: req.body, 1667 }); 1668 fetches.push( 1669 (function (request, test) { 1670 return fetch(request).then( 1671 function (res) { 1672 ok(test.pass, "Expected test to pass for " + JSON.stringify(test)); 1673 is( 1674 res.status, 1675 200, 1676 "wrong status in test for " + JSON.stringify(test) 1677 ); 1678 is( 1679 res.statusText, 1680 "OK", 1681 "wrong status text for " + JSON.stringify(test) 1682 ); 1683 is( 1684 res.type, 1685 "cors", 1686 "wrong response type for " + JSON.stringify(test) 1687 ); 1688 var reqHost = new URL(req.url).host; 1689 // If there is a service worker present, the redirections will be 1690 // transparent, assuming that the original request is to the current 1691 // site and would be intercepted. 1692 if (isSWPresent) { 1693 if (reqHost === location.host) { 1694 is( 1695 new URL(res.url).host, 1696 reqHost, 1697 "Response URL should be original URL with a SW present" 1698 ); 1699 } 1700 } else { 1701 is( 1702 new URL(res.url).host, 1703 new URL(test.hops[test.hops.length - 1].server).host, 1704 "Response URL should be redirected URL" 1705 ); 1706 } 1707 return res.text().then(function (v) { 1708 is( 1709 v, 1710 "<res>hello pass</res>\n", 1711 "wrong responseText in test for " + JSON.stringify(test) 1712 ); 1713 }); 1714 }, 1715 function (e) { 1716 ok(!test.pass, "Expected test failure for " + JSON.stringify(test)); 1717 ok( 1718 e instanceof TypeError, 1719 "Exception should be TypeError for " + JSON.stringify(test) 1720 ); 1721 } 1722 ); 1723 })(request, test) 1724 ); 1725 } 1726 1727 return Promise.all(fetches); 1728 } 1729 1730 function testNoCORSRedirects() { 1731 var origin = self.location.origin; 1732 1733 var tests = [ 1734 { pass: 1, method: "GET", hops: [{ server: "https://example.net" }] }, 1735 { 1736 pass: 1, 1737 method: "GET", 1738 hops: [{ server: origin }, { server: "https://example.net" }], 1739 }, 1740 { 1741 pass: 1, 1742 method: "GET", 1743 // Must use a simple header due to no-cors header restrictions. 1744 headers: { "accept-language": "en-us" }, 1745 hops: [{ server: origin }, { server: "https://example.net" }], 1746 }, 1747 { 1748 pass: 1, 1749 method: "GET", 1750 hops: [ 1751 { server: origin }, 1752 { server: "https://example.net" }, 1753 { server: origin }, 1754 ], 1755 }, 1756 { 1757 pass: 1, 1758 method: "POST", 1759 body: "upload body here", 1760 hops: [{ server: origin }, { server: "https://example.net" }], 1761 }, 1762 { 1763 pass: 0, 1764 method: "DELETE", 1765 hops: [{ server: origin }, { server: "https://example.net" }], 1766 }, 1767 ]; 1768 1769 var fetches = []; 1770 for (test of tests) { 1771 req = { 1772 url: 1773 test.hops[0].server + 1774 corsServerPath + 1775 "hop=1&hops=" + 1776 escape(JSON.stringify(test.hops)), 1777 method: test.method, 1778 headers: test.headers, 1779 body: test.body, 1780 }; 1781 1782 if (test.headers) { 1783 req.url += "&headers=" + escape(JSON.stringify(test.headers)); 1784 } 1785 1786 if (test.pass) { 1787 if (test.body) { 1788 req.url += "&body=" + escape(test.body); 1789 } 1790 } 1791 1792 fetches.push( 1793 (function (req, test) { 1794 return new Promise(function (resolve, reject) { 1795 resolve( 1796 new Request(req.url, { 1797 mode: "no-cors", 1798 method: req.method, 1799 headers: req.headers, 1800 body: req.body, 1801 }) 1802 ); 1803 }) 1804 .then(function (request) { 1805 return fetch(request); 1806 }) 1807 .then( 1808 function (res) { 1809 ok( 1810 test.pass, 1811 "Expected test to pass for " + JSON.stringify(test) 1812 ); 1813 // All requests are cross-origin no-cors, we should always have 1814 // an opaque response here. All values on the opaque response 1815 // should be hidden. 1816 is( 1817 res.type, 1818 "opaque", 1819 "wrong response type for " + JSON.stringify(test) 1820 ); 1821 is( 1822 res.status, 1823 0, 1824 "wrong status in test for " + JSON.stringify(test) 1825 ); 1826 is( 1827 res.statusText, 1828 "", 1829 "wrong status text for " + JSON.stringify(test) 1830 ); 1831 is(res.url, "", "wrong response url for " + JSON.stringify(test)); 1832 return res.text().then(function (v) { 1833 is( 1834 v, 1835 "", 1836 "wrong responseText in test for " + JSON.stringify(test) 1837 ); 1838 }); 1839 }, 1840 function (e) { 1841 ok( 1842 !test.pass, 1843 "Expected test failure for " + JSON.stringify(test) 1844 ); 1845 ok( 1846 e instanceof TypeError, 1847 "Exception should be TypeError for " + JSON.stringify(test) 1848 ); 1849 } 1850 ); 1851 })(req, test) 1852 ); 1853 } 1854 1855 return Promise.all(fetches); 1856 } 1857 1858 function testReferrer() { 1859 var referrer; 1860 if (self && self.location) { 1861 referrer = self.location.href; 1862 } else { 1863 referrer = document.documentURI; 1864 } 1865 1866 var dict = { 1867 Referer: referrer, 1868 }; 1869 return fetch( 1870 corsServerPath + "headers=" + encodeURIComponent(JSON.stringify(dict)) 1871 ).then( 1872 function (res) { 1873 is(res.status, 200, "expected correct referrer header to be sent"); 1874 dump(res.statusText); 1875 }, 1876 function (e) { 1877 ok(false, "expected correct referrer header to be sent"); 1878 } 1879 ); 1880 } 1881 1882 function runTest() { 1883 testNoCorsCtor(); 1884 let promise = Promise.resolve(); 1885 if (typeof SpecialPowers === "object") { 1886 promise = SpecialPowers.pushPrefEnv({ 1887 // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default" 1888 set: [["network.cookie.sameSite.laxByDefault", false]], 1889 }); 1890 } 1891 1892 return promise 1893 .then(testModeSameOrigin) 1894 .then(testModeNoCors) 1895 .then(testModeCors) 1896 .then(testSameOriginCredentials) 1897 .then(testCrossOriginCredentials) 1898 .then(testModeNoCorsCredentials) 1899 .then(testCORSRedirects) 1900 .then(testNoCORSRedirects) 1901 .then(testReferrer); 1902 // Put more promise based tests here. 1903 }