benchmark_backend_server.js (5575B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 /* globals require, process, Buffer */ 8 9 const http = require("http"); 10 11 const html = ` 12 <!DOCTYPE html> 13 <html> 14 <head> 15 <title>Upload/Download test</title> 16 <style> 17 html { 18 font-family: neo-sans; 19 font-weight: 700; 20 font-size: calc(42rem / 16); 21 } 22 body { 23 background: white; 24 } 25 section { 26 border-radius: 1em; 27 padding: 1em; 28 position: absolute; 29 top: 50%; 30 left: 50%; 31 margin-right: -50%; 32 transform: translate(-50%, -50%); 33 } 34 </style> 35 </head> 36 <body> 37 <section> 38 Upload/Download test 39 </section> 40 <button id="fileUpload">Upload Test</button> 41 <p id="upload_status"> </p> 42 <button id="downloadBtn">Download Test</button> 43 <p id="download_status"></p> 44 <script> 45 let upload_status = ""; 46 let download_status = ""; 47 48 function set_status(id, status) { 49 if (id === "upload_status") { 50 upload_status = status; 51 } else if (id === "download_status") { 52 download_status = status; 53 } 54 console.log(id + ":" + status); 55 document.getElementById(id).innerHTML = status; 56 } 57 58 set_status("upload_status", "not_started"); 59 set_status("download_status", "not_started"); 60 61 const BLOB_SIZE = 8 * 1024 * 1024; 62 63 // Function to generate a Blob of specified size 64 function generateBlob(sizeInBytes) { 65 const array = new Uint8Array(sizeInBytes).fill(0); 66 return new Blob([array], { type: 'application/octet-stream' }); 67 } 68 69 // Function to upload the entire Blob 70 async function uploadBlob(blob) { 71 const formData = new FormData(); 72 formData.append('blob', blob); 73 74 const response = await fetch('/saveFile', { 75 method: 'POST', 76 body: formData, 77 }); 78 79 if (!response.ok) { 80 throw new Error("Failed to upload Blob"); 81 } 82 83 return response.json(); 84 } 85 86 const handleUpload = async () => { 87 const blob = generateBlob(BLOB_SIZE); 88 let totalBytesUploaded = 0; 89 set_status("upload_status", "started"); 90 const startTime = performance.now(); 91 const uploadDuration = 60 * 1000; 92 const endTime = startTime + uploadDuration; 93 94 try { 95 while (true) { 96 const currentTime = performance.now(); 97 if (currentTime >= endTime) { 98 break; 99 } 100 101 await uploadBlob(blob); 102 103 totalBytesUploaded += blob.size; 104 set_status("upload_status", "uploaded:" + totalBytesUploaded); 105 } 106 107 const actualEndTime = performance.now(); 108 const totalTimeSeconds = (actualEndTime - startTime) / 1000; 109 110 // Calculate goodput in Mbps 111 const goodputMbps = (totalBytesUploaded * 8) / (totalTimeSeconds * 1_000_000); 112 set_status("upload_status", "success goodput:" + goodputMbps); 113 } catch (error) { 114 set_status("upload_status", "error:" + error); 115 } 116 }; 117 118 document.querySelector('#fileUpload').addEventListener('click', handleUpload); 119 120 const handleDownloadTest = () => { 121 set_status("download_status", "started"); 122 const startTime = performance.now(); 123 fetch('/downloadTest') 124 .then(response => response.blob()) 125 .then(blob => { 126 const endTime = performance.now(); 127 const downloadTime = endTime - startTime; 128 set_status("download_status", "success time:" + downloadTime); 129 }) 130 .catch(error => { 131 console.error(error); 132 set_download_status("error"); 133 }); 134 } 135 document.querySelector('#downloadBtn').addEventListener('click', handleDownloadTest); 136 </script> 137 </body> 138 </html> 139 `; 140 141 const server = http.createServer((req, res) => { 142 if (req.url === "/saveFile" && req.method.toLowerCase() === "post") { 143 let totalSize = 0; 144 req.on("data", chunk => { 145 totalSize += chunk.length; 146 }); 147 148 req.on("end", () => { 149 res.writeHead(200, { "Content-Type": "application/json" }); 150 res.end(JSON.stringify({ status: "success", size: totalSize })); 151 }); 152 } else if ( 153 req.url === "/downloadTest" && 154 req.method.toLowerCase() === "get" 155 ) { 156 const contentLength = 32 * 1024 * 1024; // 32 MB 157 res.writeHead(200, { 158 "Content-Type": "application/octet-stream", 159 "Content-Length": contentLength, 160 "Content-Disposition": "attachment; filename=testfile.bin", 161 "Cache-Control": "no-store, no-cache, must-revalidate", 162 Pragma: "no-cache", 163 }); 164 165 const chunkSize = 1024 * 1024; // 1MB chunks 166 for (let i = 0; i < contentLength / chunkSize; i++) { 167 const chunk = Buffer.alloc(chunkSize, "0"); // Fill the chunk with zeros 168 res.write(chunk); 169 } 170 res.end(); 171 } else if (req.url === "/" && req.method.toLowerCase() === "get") { 172 res.writeHead(200, { 173 "Content-Type": "text/html", 174 "Cache-Control": "no-store, no-cache, must-revalidate", 175 Pragma: "no-cache", 176 }); 177 res.end(html); 178 } else { 179 res.writeHead(404, { "Content-Type": "text/plain" }); 180 res.end("Not Found"); 181 } 182 }); 183 184 server.listen(() => { 185 console.log(`Server is running on http://localhost:${server.address().port}`); 186 });