mirror of
https://github.com/godotengine/godot.git
synced 2025-10-20 00:13:30 +00:00
[HTML5] Replace XMLHttpRequest with Fetch.
This has some advantages: - Streaming/chunked response support. - Broader headers support.
This commit is contained in:
parent
751a6f9a63
commit
c01f35de12
6 changed files with 376 additions and 349 deletions
258
platform/javascript/js/libs/library_godot_fetch.js
Normal file
258
platform/javascript/js/libs/library_godot_fetch.js
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*************************************************************************/
|
||||
/* library_godot_fetch.js */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
const GodotFetch = {
|
||||
$GodotFetch__deps: ['$GodotRuntime'],
|
||||
$GodotFetch: {
|
||||
|
||||
onread: function (id, result) {
|
||||
const obj = IDHandler.get(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
if (result.value) {
|
||||
obj.chunks.push(result.value);
|
||||
}
|
||||
obj.reading = false;
|
||||
obj.done = result.done;
|
||||
},
|
||||
|
||||
onresponse: function (id, response) {
|
||||
const obj = IDHandler.get(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
let size = -1;
|
||||
let compressed = false;
|
||||
let chunked = false;
|
||||
response.headers.forEach(function (value, header) {
|
||||
const v = value.toLowerCase().trim();
|
||||
const h = header.toLowerCase().trim();
|
||||
if (h === 'content-encoding') {
|
||||
compressed = true;
|
||||
size = -1;
|
||||
} else if (h === 'content-length') {
|
||||
const len = Number.parseInt(value, 10);
|
||||
if (!Number.isNaN(len) && !compressed) {
|
||||
size = len;
|
||||
}
|
||||
} else if (h === 'transfer-encoding' && v === 'chunked') {
|
||||
chunked = true;
|
||||
}
|
||||
});
|
||||
obj.bodySize = size;
|
||||
obj.status = response.status;
|
||||
obj.response = response;
|
||||
obj.reader = response.body.getReader();
|
||||
obj.chunked = chunked;
|
||||
},
|
||||
|
||||
onerror: function (id, err) {
|
||||
GodotRuntime.error(err);
|
||||
const obj = IDHandler.get(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
obj.error = err;
|
||||
},
|
||||
|
||||
create: function (method, url, headers, body) {
|
||||
const obj = {
|
||||
request: null,
|
||||
response: null,
|
||||
reader: null,
|
||||
error: null,
|
||||
done: false,
|
||||
reading: false,
|
||||
status: 0,
|
||||
chunks: [],
|
||||
bodySize: -1,
|
||||
};
|
||||
const id = IDHandler.add(obj);
|
||||
const init = {
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: body,
|
||||
};
|
||||
obj.request = fetch(url, init);
|
||||
obj.request.then(GodotFetch.onresponse.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
|
||||
return id;
|
||||
},
|
||||
|
||||
free: function (id) {
|
||||
const obj = IDHandler.get(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
IDHandler.remove(id);
|
||||
if (!obj.request) {
|
||||
return;
|
||||
}
|
||||
// Try to abort
|
||||
obj.request.then(function (response) {
|
||||
response.abort();
|
||||
}).catch(function (e) { /* nothing to do */ });
|
||||
},
|
||||
|
||||
read: function (id) {
|
||||
const obj = IDHandler.get(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
if (obj.reader && !obj.reading) {
|
||||
if (obj.done) {
|
||||
obj.reader = null;
|
||||
return;
|
||||
}
|
||||
obj.reading = true;
|
||||
obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
godot_js_fetch_create__sig: 'iii',
|
||||
godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {
|
||||
const method = GodotRuntime.parseString(p_method);
|
||||
const url = GodotRuntime.parseString(p_url);
|
||||
const headers = GodotRuntime.parseStringArray(p_headers, p_headers_size);
|
||||
const body = p_body_size ? GodotRuntime.heapSlice(HEAP8, p_body, p_body_size) : null;
|
||||
return GodotFetch.create(method, url, headers.map(function (hv) {
|
||||
const idx = hv.indexOf(':');
|
||||
if (idx <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
hv.slice(0, idx).trim(),
|
||||
hv.slice(idx + 1).trim(),
|
||||
];
|
||||
}).filter(function (v) {
|
||||
return v.length === 2;
|
||||
}), body);
|
||||
},
|
||||
|
||||
godot_js_fetch_state_get__sig: 'ii',
|
||||
godot_js_fetch_state_get: function (p_id) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj) {
|
||||
return -1;
|
||||
}
|
||||
if (obj.error) {
|
||||
return -1;
|
||||
}
|
||||
if (!obj.response) {
|
||||
return 0;
|
||||
}
|
||||
if (obj.reader) {
|
||||
return 1;
|
||||
}
|
||||
if (obj.done) {
|
||||
return 2;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
godot_js_fetch_http_status_get__sig: 'ii',
|
||||
godot_js_fetch_http_status_get: function (p_id) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj || !obj.response) {
|
||||
return 0;
|
||||
}
|
||||
return obj.status;
|
||||
},
|
||||
|
||||
godot_js_fetch_read_headers__sig: 'iii',
|
||||
godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj || !obj.response) {
|
||||
return 1;
|
||||
}
|
||||
const cb = GodotRuntime.get_func(p_parse_cb);
|
||||
const arr = [];
|
||||
obj.response.headers.forEach(function (v, h) {
|
||||
arr.push(`${h}:${v}`);
|
||||
});
|
||||
const c_ptr = GodotRuntime.allocStringArray(arr);
|
||||
cb(arr.length, c_ptr, p_ref);
|
||||
GodotRuntime.freeStringArray(c_ptr, arr.length);
|
||||
return 0;
|
||||
},
|
||||
|
||||
godot_js_fetch_read_chunk__sig: 'ii',
|
||||
godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj || !obj.response) {
|
||||
return 0;
|
||||
}
|
||||
let to_read = p_buf_size;
|
||||
const chunks = obj.chunks;
|
||||
while (to_read && chunks.length) {
|
||||
const chunk = obj.chunks[0];
|
||||
if (chunk.length > to_read) {
|
||||
GodotRuntime.heapCopy(HEAP8, chunk.slice(0, to_read), p_buf);
|
||||
chunks[0] = chunk.slice(to_read);
|
||||
to_read = 0;
|
||||
} else {
|
||||
GodotRuntime.heapCopy(HEAP8, chunk, p_buf);
|
||||
to_read -= chunk.length;
|
||||
chunks.pop();
|
||||
}
|
||||
}
|
||||
if (!chunks.length) {
|
||||
GodotFetch.read(p_id);
|
||||
}
|
||||
return p_buf_size - to_read;
|
||||
},
|
||||
|
||||
godot_js_fetch_body_length_get__sig: 'ii',
|
||||
godot_js_fetch_body_length_get: function (p_id) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj || !obj.response) {
|
||||
return -1;
|
||||
}
|
||||
return obj.bodySize;
|
||||
},
|
||||
|
||||
godot_js_fetch_is_chunked__sig: 'ii',
|
||||
godot_js_fetch_is_chunked: function (p_id) {
|
||||
const obj = IDHandler.get(p_id);
|
||||
if (!obj || !obj.response) {
|
||||
return -1;
|
||||
}
|
||||
return obj.chunked ? 1 : 0;
|
||||
},
|
||||
|
||||
godot_js_fetch_free__sig: 'vi',
|
||||
godot_js_fetch_free: function (id) {
|
||||
GodotFetch.free(id);
|
||||
},
|
||||
};
|
||||
|
||||
autoAddDeps(GodotFetch, '$GodotFetch');
|
||||
mergeInto(LibraryManager.library, GodotFetch);
|
Loading…
Add table
Add a link
Reference in a new issue