send-redirect-post-upload.htm (6545B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <link rel="help" href="https://xhr.spec.whatwg.org/#handler-xhr-onprogress" data-tested-assertations="../.." /> 5 <link rel="help" href="https://xhr.spec.whatwg.org/#event-xhr-progress" data-tested-assertations="../.." /> 6 <link rel="help" href="https://xhr.spec.whatwg.org/#the-send()-method" data-tested-assertations="following::dt[@id="dom-xmlhttprequest-send-bodyinit"]/following::dd[1]/p[2] following::ol[1]/li[9]//li[1] following::ol[1]/li[9]//li[2]" /> 7 <link rel="help" href="https://fetch.spec.whatwg.org/#http-fetch" data-tested-assertations="following::ol[1]/li[6]/dl/dd[1]//dd[3]" /> 8 <link rel="help" href="https://fetch.spec.whatwg.org/#concept-http-redirect-fetch" data-tested-assertations="following::li[16]" /> 9 <script src="/resources/testharness.js"></script> 10 <script src="/resources/testharnessreport.js"></script> 11 <title>XMLHttpRequest: The send() method: POSTing to URL that redirects</title> 12 </head> 13 14 <body> 15 <div id="log"></div> 16 17 <script type="text/javascript"> 18 function testRedirectPost(params) { 19 var test = async_test(document.title + " (" + params.name + ")"); 20 var actual = []; 21 // We check upload.onprogress with a boolean because it *might* fire more than once 22 var progressFiredReadyState1 = false; 23 24 var expectedHeaders, expectedEvents; 25 26 // 307 redirects should resend the POST data, and events and headers will be a little different.. 27 if(params.expectResendPost) { 28 expectedHeaders = { 29 "X-Request-Content-Length": "12000", 30 "X-Request-Content-Type": "text/plain;charset=UTF-8", 31 "X-Request-Method": "POST", 32 "X-Request-Query": "NO", 33 "Content-Length": "12000" 34 } 35 expectedEvents = [ 36 "xhr onreadystatechange 1", 37 "xhr loadstart 1", 38 "upload loadstart 1", 39 "upload loadend 1", 40 "xhr onreadystatechange 2", 41 "xhr onreadystatechange 3", 42 "xhr onreadystatechange 4", 43 "xhr load 4", 44 "xhr loadend 4" 45 ]; 46 } else { 47 // setting the right expectations for POST resent as GET without request body 48 expectedHeaders = { 49 "X-Request-Content-Length": "NO", 50 "X-Request-Content-Type": "NO", 51 "X-Request-Method": "GET", 52 "X-Request-Query": "NO" 53 } 54 expectedEvents = [ 55 "xhr onreadystatechange 1", 56 "xhr loadstart 1", 57 "upload loadstart 1", 58 "upload loadend 1", 59 "xhr onreadystatechange 2", 60 /* we expect no onreadystatechange readyState=3 event because there is no loading content */ 61 "xhr onreadystatechange 4", 62 "xhr load 4", 63 "xhr loadend 4" 64 ]; 65 } 66 // Override expectations if provided. 67 if(params.expectedContentType) 68 expectedHeaders["X-Request-Content-Type"] = params.expectedContentType; 69 70 test.step(function() 71 { 72 var xhr = new XMLHttpRequest(); 73 74 xhr.upload.onloadstart = test.step_func(function(e) { 75 actual.push("upload loadstart " + xhr.readyState); 76 }); 77 xhr.upload.onprogress = test.step_func(function(e) { 78 // events every 50ms, one final when uploading is done 79 if(xhr.readyState >= xhr.HEADERS_RECEIVED) { 80 assert_equals(xhr.status, 200, "JS never gets to see the 30x status code"); 81 } 82 progressFiredReadyState1 = xhr.readyState === xhr.OPENED; 83 }); 84 xhr.upload.onloadend = test.step_func(function() { 85 actual.push("upload loadend " + xhr.readyState); 86 }); 87 xhr.onloadstart = test.step_func(function() { 88 actual.push("xhr loadstart " + xhr.readyState); 89 }); 90 xhr.onreadystatechange = test.step_func(function() { 91 if(xhr.readyState >= xhr.HEADERS_RECEIVED) { 92 assert_equals(xhr.status, 200, "JS never gets to see the 30x status code"); 93 } 94 95 // The UA may fire multiple "readystatechange" events while in 96 // the "loading" state. 97 // https://xhr.spec.whatwg.org/#the-send()-method 98 if (xhr.readyState === 3 && actual[actual.length - 1] === "xhr onreadystatechange 3") { 99 return; 100 } 101 102 actual.push("xhr onreadystatechange " + xhr.readyState); 103 }); 104 xhr.onload = test.step_func(function(e) 105 { 106 actual.push("xhr load " + xhr.readyState); 107 }); 108 xhr.onloadend = test.step_func(function(e) 109 { 110 actual.push("xhr loadend " + xhr.readyState); 111 112 assert_true(progressFiredReadyState1, "One progress event should fire on xhr.upload when readyState is 1"); 113 114 // Headers will tell us if data was sent when expected 115 for(var header in expectedHeaders) { 116 assert_equals(xhr.getResponseHeader(header), expectedHeaders[header], header); 117 } 118 119 assert_array_equals(actual, expectedEvents, "events firing in expected order and states"); 120 if (params.expectedBody) 121 assert_equals(xhr.response, params.expectedBody, 'request body was resent'); 122 test.done(); 123 }); 124 125 xhr.open("POST", "./resources/redirect.py?location=content.py&code=" + params.code, true); 126 xhr.send(params.body); 127 }); 128 } 129 130 const stringBody = "Test Message".repeat(1000); 131 const blobBody = new Blob(new Array(1000).fill("Test Message")); 132 133 testRedirectPost({name: "301", code: 301, expectResendPost: false, body: stringBody}); 134 testRedirectPost({name: "302", code: 302, expectResendPost: false, body: stringBody}); 135 testRedirectPost({name: "303", code: 303, expectResendPost: false, body: stringBody}); 136 testRedirectPost({name: "307 (string)", code: 307, expectResendPost: true, body: stringBody, expectedBody: stringBody }); 137 testRedirectPost({name: "307 (blob)", code: 307, expectResendPost: true, body: blobBody, expectedBody: stringBody, expectedContentType: "NO" }); 138 </script> 139 </body> 140 </html>