Path: blob/master/platform/web/js/libs/library_godot_fetch.js
10279 views
/**************************************************************************/1/* library_godot_fetch.js */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930const GodotFetch = {31$GodotFetch__deps: ['$IDHandler', '$GodotRuntime'],32$GodotFetch: {3334onread: function (id, result) {35const obj = IDHandler.get(id);36if (!obj) {37return;38}39if (result.value) {40obj.chunks.push(result.value);41}42obj.reading = false;43obj.done = result.done;44},4546onresponse: function (id, response) {47const obj = IDHandler.get(id);48if (!obj) {49return;50}51let chunked = false;52response.headers.forEach(function (value, header) {53const v = value.toLowerCase().trim();54const h = header.toLowerCase().trim();55if (h === 'transfer-encoding' && v === 'chunked') {56chunked = true;57}58});59obj.status = response.status;60obj.response = response;61// `body` can be null per spec (for example, in cases where the request method is HEAD).62// As of the time of writing, Chromium (127.0.6533.72) does not follow the spec but Firefox (131.0.3) does.63// See godotengine/godot#76825 for more information.64// See Chromium revert (of the change to follow the spec):65// https://chromium.googlesource.com/chromium/src/+/135354b7bdb554cd03c913af7c90aceead03c4d466obj.reader = response.body?.getReader();67obj.chunked = chunked;68},6970onerror: function (id, err) {71GodotRuntime.error(err);72const obj = IDHandler.get(id);73if (!obj) {74return;75}76obj.error = err;77},7879create: function (method, url, headers, body) {80const obj = {81request: null,82response: null,83reader: null,84error: null,85done: false,86reading: false,87status: 0,88chunks: [],89};90const id = IDHandler.add(obj);91const init = {92method: method,93headers: headers,94body: body,95};96obj.request = fetch(url, init);97obj.request.then(GodotFetch.onresponse.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));98return id;99},100101free: function (id) {102const obj = IDHandler.get(id);103if (!obj) {104return;105}106IDHandler.remove(id);107if (!obj.request) {108return;109}110// Try to abort111obj.request.then(function (response) {112response.abort();113}).catch(function (e) { /* nothing to do */ });114},115116read: function (id) {117const obj = IDHandler.get(id);118if (!obj) {119return;120}121if (obj.reader && !obj.reading) {122if (obj.done) {123obj.reader = null;124return;125}126obj.reading = true;127obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));128} else if (obj.reader == null && obj.response.body == null) {129// Emulate a stream closure to maintain the request lifecycle.130obj.reading = true;131GodotFetch.onread(id, { value: undefined, done: true });132}133},134},135136godot_js_fetch_create__proxy: 'sync',137godot_js_fetch_create__sig: 'iiiiiii',138godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {139const method = GodotRuntime.parseString(p_method);140const url = GodotRuntime.parseString(p_url);141const headers = GodotRuntime.parseStringArray(p_headers, p_headers_size);142const body = p_body_size ? GodotRuntime.heapSlice(HEAP8, p_body, p_body_size) : null;143return GodotFetch.create(method, url, headers.map(function (hv) {144const idx = hv.indexOf(':');145if (idx <= 0) {146return [];147}148return [149hv.slice(0, idx).trim(),150hv.slice(idx + 1).trim(),151];152}).filter(function (v) {153return v.length === 2;154}), body);155},156157godot_js_fetch_state_get__proxy: 'sync',158godot_js_fetch_state_get__sig: 'ii',159godot_js_fetch_state_get: function (p_id) {160const obj = IDHandler.get(p_id);161if (!obj) {162return -1;163}164if (obj.error) {165return -1;166}167if (!obj.response) {168return 0;169}170// If the reader is nullish, but there is no body, and the request is not marked as done,171// the same status should be returned as though the request is currently being read172// so that the proper lifecycle closure can be handled in `read()`.173if (obj.reader || (obj.response.body == null && !obj.done)) {174return 1;175}176if (obj.done) {177return 2;178}179return -1;180},181182godot_js_fetch_http_status_get__proxy: 'sync',183godot_js_fetch_http_status_get__sig: 'ii',184godot_js_fetch_http_status_get: function (p_id) {185const obj = IDHandler.get(p_id);186if (!obj || !obj.response) {187return 0;188}189return obj.status;190},191192godot_js_fetch_read_headers__proxy: 'sync',193godot_js_fetch_read_headers__sig: 'iiii',194godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {195const obj = IDHandler.get(p_id);196if (!obj || !obj.response) {197return 1;198}199const cb = GodotRuntime.get_func(p_parse_cb);200const arr = [];201obj.response.headers.forEach(function (v, h) {202arr.push(`${h}:${v}`);203});204const c_ptr = GodotRuntime.allocStringArray(arr);205cb(arr.length, c_ptr, p_ref);206GodotRuntime.freeStringArray(c_ptr, arr.length);207return 0;208},209210godot_js_fetch_read_chunk__proxy: 'sync',211godot_js_fetch_read_chunk__sig: 'iiii',212godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {213const obj = IDHandler.get(p_id);214if (!obj || !obj.response) {215return 0;216}217let to_read = p_buf_size;218const chunks = obj.chunks;219while (to_read && chunks.length) {220const chunk = obj.chunks[0];221if (chunk.length > to_read) {222GodotRuntime.heapCopy(HEAP8, chunk.slice(0, to_read), p_buf);223chunks[0] = chunk.slice(to_read);224to_read = 0;225} else {226GodotRuntime.heapCopy(HEAP8, chunk, p_buf);227to_read -= chunk.length;228chunks.pop();229}230}231if (!chunks.length) {232GodotFetch.read(p_id);233}234return p_buf_size - to_read;235},236237godot_js_fetch_is_chunked__proxy: 'sync',238godot_js_fetch_is_chunked__sig: 'ii',239godot_js_fetch_is_chunked: function (p_id) {240const obj = IDHandler.get(p_id);241if (!obj || !obj.response) {242return -1;243}244return obj.chunked ? 1 : 0;245},246247godot_js_fetch_free__proxy: 'sync',248godot_js_fetch_free__sig: 'vi',249godot_js_fetch_free: function (id) {250GodotFetch.free(id);251},252};253254autoAddDeps(GodotFetch, '$GodotFetch');255mergeInto(LibraryManager.library, GodotFetch);256257258