-
Notifications
You must be signed in to change notification settings - Fork 3.4k
[file-packager] Use fetch()
for loading file packages.
#22016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
32fbce8
b88154d
b8e2ed9
58fcb8f
3945a0b
c31c0f8
b76122f
9712210
ba19ffc
30d2d8e
0c88d25
0e769ca
b23fffa
c3fadbc
4b05f80
8a027b7
9b415a6
db4a31d
3bf0964
03b301a
8ff77d0
5b37c95
34b1afd
bedece9
6bffc95
e2c4ea8
efaf744
027532e
e360fa8
295b750
27a84da
206bb2b
4168338
f622a0d
e24cc12
489bc06
071b814
ddb958e
c553d28
61dea12
661258b
06404fb
68d3d02
9b00c17
e78ac48
27a1c42
edd3a18
a7747d4
78958aa
572bf74
4386290
e68044e
4fac7e0
1c9d521
aadfaec
6fd7e01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -359,7 +359,7 @@ def generate_object_file(data_files): | |
|
||
def main(): | ||
if len(sys.argv) == 1: | ||
err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] | ||
err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] | ||
See the source for more details.''') | ||
return 1 | ||
|
||
|
@@ -952,54 +952,62 @@ def generate_js(data_target, data_files, metadata): | |
}); | ||
return; | ||
}'''.strip() | ||
|
||
ret += ''' | ||
function fetchRemotePackage(packageName, packageSize, callback, errback) { | ||
%(node_support_code)s | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open('GET', packageName, true); | ||
xhr.responseType = 'arraybuffer'; | ||
xhr.onprogress = (event) => { | ||
var url = packageName; | ||
var size = packageSize; | ||
if (event.total) size = event.total; | ||
if (event.loaded) { | ||
if (!xhr.addedTotal) { | ||
xhr.addedTotal = true; | ||
if (!Module['dataFileDownloads']) Module['dataFileDownloads'] = {}; | ||
Module['dataFileDownloads'][url] = { | ||
loaded: event.loaded, | ||
total: size | ||
}; | ||
} else { | ||
Module['dataFileDownloads'][url].loaded = event.loaded; | ||
Module['dataFileDownloads'] ??= {}; | ||
fetch(packageName) | ||
.catch((cause) => Promise.reject(new Error(`Network Error: ${packageName}`, {cause}))) // If fetch fails, rewrite the error to include the failing URL & the cause. | ||
.then((response) => { | ||
if (!response.ok) { | ||
return Promise.reject(new Error(`${response.status}: ${response.url}`)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should be calling |
||
} | ||
var total = 0; | ||
var loaded = 0; | ||
var num = 0; | ||
for (var download in Module['dataFileDownloads']) { | ||
var data = Module['dataFileDownloads'][download]; | ||
total += data.total; | ||
loaded += data.loaded; | ||
num++; | ||
|
||
if (!response.body && response.arrayBuffer) { // If we're using the polyfill, readers won't be available... | ||
return response.arrayBuffer().then(callback); | ||
} | ||
total = Math.ceil(total * Module['expectedDataFileDownloads']/num); | ||
Module['setStatus']?.(`Downloading data... (${loaded}/${total})`); | ||
} else if (!Module['dataFileDownloads']) { | ||
|
||
const reader = response.body.getReader(); | ||
const iterate = () => reader.read().then(handleChunk).catch((cause) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this i.e. will folks still be able to render a process bar based on this? @juj (since I think you use the progress feature). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I recall correctly the browser will call this roughly every few kilobytes. I think it maps to the size of the chunks after the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this true for the XHR onprogress callback too? i.e. we are going to regress users who expect progress callback but don't use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For a file of 13_631_488 bytes without the 2097152 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sbc100 I checked out the |
||
return Promise.reject(new Error(`Unexpected error while handling : ${response.url} ${cause}`, {cause})); | ||
}); | ||
|
||
const chunks = []; | ||
const headers = response.headers; | ||
const total = Number(headers.get('Content-Length') ?? packageSize); | ||
let loaded = 0; | ||
|
||
const handleChunk = ({done, value}) => { | ||
if (!done) { | ||
chunks.push(value); | ||
loaded += value.length; | ||
Module['dataFileDownloads'][packageName] = {loaded, total}; | ||
|
||
let totalLoaded = 0; | ||
let totalSize = 0; | ||
|
||
for (const download of Object.values(Module['dataFileDownloads'])) { | ||
totalLoaded += download.loaded; | ||
totalSize += download.total; | ||
} | ||
|
||
Module['setStatus']?.(`Downloading data... (${totalLoaded}/${totalSize})`); | ||
return iterate(); | ||
} else { | ||
const packageData = new Uint8Array(chunks.map((c) => c.length).reduce((a, b) => a + b, 0)); | ||
let offset = 0; | ||
for (const chunk of chunks) { | ||
packageData.set(chunk, offset); | ||
offset += chunk.length; | ||
} | ||
callback(packageData.buffer); | ||
} | ||
}; | ||
|
||
Module['setStatus']?.('Downloading data...'); | ||
} | ||
}; | ||
xhr.onerror = (event) => { | ||
throw new Error("NetworkError for: " + packageName); | ||
} | ||
xhr.onload = (event) => { | ||
if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 | ||
var packageData = xhr.response; | ||
callback(packageData); | ||
} else { | ||
throw new Error(xhr.statusText + " : " + xhr.responseURL); | ||
} | ||
}; | ||
xhr.send(null); | ||
return iterate(); | ||
}); | ||
}; | ||
|
||
function handleError(error) { | ||
|
@@ -1097,15 +1105,14 @@ def generate_js(data_target, data_files, metadata): | |
function runMetaWithFS() { | ||
Module['addRunDependency']('%(metadata_file)s'); | ||
var REMOTE_METADATA_NAME = Module['locateFile'] ? Module['locateFile']('%(metadata_file)s', '') : '%(metadata_file)s'; | ||
var xhr = new XMLHttpRequest(); | ||
xhr.onreadystatechange = () => { | ||
if (xhr.readyState === 4 && xhr.status === 200) { | ||
loadPackage(JSON.parse(xhr.responseText)); | ||
} | ||
} | ||
xhr.open('GET', REMOTE_METADATA_NAME, true); | ||
xhr.overrideMimeType('application/json'); | ||
xhr.send(null); | ||
fetch(REMOTE_METADATA_NAME) | ||
.then((response) => { | ||
if (response.ok) { | ||
return response.json(); | ||
} | ||
return Promise.reject(new Error(`${response.status}: ${response.url}`)); | ||
}) | ||
.then(loadPackage); | ||
} | ||
|
||
if (Module['calledRun']) { | ||
|
@@ -1114,7 +1121,6 @@ def generate_js(data_target, data_files, metadata): | |
if (!Module['preRun']) Module['preRun'] = []; | ||
Module["preRun"].push(runMetaWithFS); | ||
}\n''' % {'metadata_file': os.path.basename(options.jsoutput + '.metadata')} | ||
|
||
else: | ||
_metadata_template = ''' | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than require special shell can you just rely on the fact that default reporting will try bot
fetch
andorigFetch
:emscripten/test/browser_reporting.js
Line 16 in a6e1f5c
i.e. when you disable
fetch
you can cache the original fetch so that that browser reporting doesn't break.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sbc100 I'm not able to get the exception out of the browser without the custom shell. It just hangs with the exception listed as uncaught in the browser's console.