partial_content.sjs (6183B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Debug and Error wrapper functions for dump(). 8 */ 9 function ERR(response, responseCode, responseCodeStr, msg) { 10 // Reset state var. 11 setState("expectedRequestType", ""); 12 // Dump to console log and send to client in response. 13 dump("SERVER ERROR: " + msg + "\n"); 14 response.write("HTTP/1.1 " + responseCode + " " + responseCodeStr + "\r\n"); 15 response.write("Content-Type: text/html; charset=UTF-8\r\n"); 16 response.write("Content-Length: " + msg.length + "\r\n"); 17 response.write("\r\n"); 18 response.write(msg); 19 } 20 21 function DBG(msg) { 22 // Dump to console only. 23 dump("SERVER DEBUG: " + msg + "\n"); 24 } 25 26 /* Delivers content in parts to test partially cached content: requires two 27 * requests for the same resource. 28 * 29 * First call will respond with partial content, but a 200 header and 30 * Content-Length equal to the full content length. No Range or If-Range 31 * headers are allowed in the request. 32 * 33 * Second call will require Range and If-Range in the request headers, and 34 * will respond with the range requested. 35 */ 36 function handleRequest(request, response) { 37 DBG("Trying to seize power"); 38 response.seizePower(); 39 40 DBG("About to check state vars"); 41 // Get state var to determine if this is the first or second request. 42 var expectedRequestType; 43 var lastModified; 44 if (getState("expectedRequestType") === "") { 45 DBG("First call: Should be requesting full content."); 46 expectedRequestType = "fullRequest"; 47 // Set state var for second request. 48 setState("expectedRequestType", "partialRequest"); 49 // Create lastModified variable for responses. 50 lastModified = new Date().toUTCString(); 51 setState("lastModified", lastModified); 52 } else if (getState("expectedRequestType") === "partialRequest") { 53 DBG("Second call: Should be requesting undelivered content."); 54 expectedRequestType = "partialRequest"; 55 // Reset state var for first request. 56 setState("expectedRequestType", ""); 57 // Get last modified date and reset state var. 58 lastModified = getState("lastModified"); 59 } else { 60 ERR( 61 response, 62 500, 63 "Internal Server Error", 64 'Invalid expectedRequestType "' + 65 expectedRequestType + 66 '"in ' + 67 "server state db." 68 ); 69 return; 70 } 71 72 // Look for Range and If-Range 73 var range = request.hasHeader("Range") ? request.getHeader("Range") : ""; 74 var ifRange = request.hasHeader("If-Range") 75 ? request.getHeader("If-Range") 76 : ""; 77 78 if (expectedRequestType === "fullRequest") { 79 // Should not have Range or If-Range in first request. 80 if (range && range.length) { 81 ERR( 82 response, 83 400, 84 "Bad Request", 85 'Should not receive "Range: ' + range + '" for first, full request.' 86 ); 87 return; 88 } 89 if (ifRange && ifRange.length) { 90 ERR( 91 response, 92 400, 93 "Bad Request", 94 'Should not receive "Range: ' + range + '" for first, full request.' 95 ); 96 return; 97 } 98 } else if (expectedRequestType === "partialRequest") { 99 // Range AND If-Range should both be present in second request. 100 if (!range) { 101 ERR( 102 response, 103 400, 104 "Bad Request", 105 'Should receive "Range: " for second, partial request.' 106 ); 107 return; 108 } 109 if (!ifRange) { 110 ERR( 111 response, 112 400, 113 "Bad Request", 114 'Should receive "If-Range: " for second, partial request.' 115 ); 116 return; 117 } 118 } else { 119 // Somewhat redundant, but a check for errors in this test code. 120 ERR( 121 response, 122 500, 123 "Internal Server Error", 124 'expectedRequestType not set correctly: "' + expectedRequestType + '"' 125 ); 126 return; 127 } 128 129 // Prepare content in two parts for responses. 130 var partialContent = 131 '<html><head></head><body><p id="firstResponse">First response</p>'; 132 var remainderContent = 133 '<p id="secondResponse">Second response</p></body></html>'; 134 var totalLength = partialContent.length + remainderContent.length; 135 136 DBG("totalLength: " + totalLength); 137 138 // Prepare common headers for the two responses. 139 let date = new Date(); 140 DBG("Date: " + date.toUTCString() + ", Last-Modified: " + lastModified); 141 var commonHeaders = 142 "Date: " + 143 date.toUTCString() + 144 "\r\n" + 145 "Last-Modified: " + 146 lastModified + 147 "\r\n" + 148 "Content-Type: text/html; charset=UTF-8\r\n" + 149 "ETag: abcd0123\r\n" + 150 "Accept-Ranges: bytes\r\n"; 151 152 // Prepare specific headers and content for first and second responses. 153 if (expectedRequestType === "fullRequest") { 154 DBG("First response: Sending partial content with a full header"); 155 response.write("HTTP/1.1 200 OK\r\n"); 156 response.write(commonHeaders); 157 // Set Content-Length to full length of resource. 158 response.write("Content-Length: " + totalLength + "\r\n"); 159 response.write("\r\n"); 160 response.write(partialContent); 161 } else if (expectedRequestType === "partialRequest") { 162 DBG("Second response: Sending remaining content with a range header"); 163 response.write("HTTP/1.1 206 Partial Content\r\n"); 164 response.write(commonHeaders); 165 // Set Content-Length to length of bytes transmitted. 166 response.write("Content-Length: " + remainderContent.length + "\r\n"); 167 response.write( 168 "Content-Range: bytes " + 169 partialContent.length + 170 "-" + 171 (totalLength - 1) + 172 "/" + 173 totalLength + 174 "\r\n" 175 ); 176 response.write("\r\n"); 177 response.write(remainderContent); 178 } else { 179 // Somewhat redundant, but a check for errors in this test code. 180 ERR( 181 response, 182 500, 183 "Internal Server Error", 184 "Something very bad happened here: expectedRequestType is invalid " + 185 'towards the end of handleRequest! - "' + 186 expectedRequestType + 187 '"' 188 ); 189 return; 190 } 191 192 response.finish(); 193 }