From e2b50db5fa701a1ecb6619f7c59362e13f16bc8c Mon Sep 17 00:00:00 2001 From: ading2210 Date: Sun, 10 Mar 2024 03:53:59 -0400 Subject: [PATCH] support all request body types --- CHANGELOG.md | 2 ++ README.md | 12 +++++++++++ client/javascript/copyright.js | 23 ++++++++------------ client/javascript/main.js | 37 +++++++++++++++----------------- client/javascript/util.js | 20 ++++------------- client/javascript/ws_polyfill.js | 22 ++++++++++--------- 6 files changed, 56 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c79b9fb..7dbce63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## v0.5.0 (3/8/24): - Added support for streaming HTTP responses via a readable stream +- Improve compatibility for older browsers +- Support for all types of fetch request bodies ## v0.4.2 (3/7/24): - Expose a function to get error strings diff --git a/README.md b/README.md index 74e4912..b502e22 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This is an experimental port of [libcurl](https://curl.se/libcurl/) to WebAssemb * [Getting Version Info](#getting-version-info) * [Getting the CA Certificates Bundle](#getting-the-ca-certificates-bundle) - [Proxy Server](#proxy-server) +- [Project Structure](#project-structure) - [Copyright](#copyright) * [Copyright Notice](#copyright-notice) @@ -223,6 +224,17 @@ server/run.sh --static=./client For a full list of server arguments, see the [wisp-server-python documentation](https://github.com/MercuryWorkshop/wisp-server-python). +## Project Structure: +- `client` - Contains all the client-side code. + - `fragments` - Various patches for the JS that emscripten produces. The script which does the patching can be found at `client/tools/patch_js.py`. + - `javascript` - All the code for the Javascript API, and for interfacing with the compiled C code. + - `libcurl` - The C code that interfaces with the libcurl library and gets compiled by emscripten. + - `tests` - Unit tests and the scripts for running them. + - `tools` - Helper shell scripts for the build process, and for compiling the various C libraries. + - `wisp_client` - A submodule for the Wisp client library. +- `server` - Contains all the server-side code for running the websocket proxy server. + - `wisp_sever` - A submodule for the Python Wisp server. + ## Copyright: This project is licensed under the GNU AGPL v3. diff --git a/client/javascript/copyright.js b/client/javascript/copyright.js index 9ad6c3e..61d1184 100644 --- a/client/javascript/copyright.js +++ b/client/javascript/copyright.js @@ -1,15 +1,10 @@ -const copyright_notice = `ading2210/libcurl.js - A port of libcurl to WASM for use in the browser. -Copyright (C) 2023 ading2210 +const copyright_notice = `libcurl.js is licensed under the GNU AGPL v3. You can find the license text and source code at the project's git repository: https://github.com/ading2210/libcurl.js -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see .`; \ No newline at end of file +Several C libraries are used, and their licenses are listed below: +- libcurl: curl License (https://curl.se/docs/copyright.html) +- openssl: Apache License 2.0 (https://github.com/openssl/openssl/blob/master/LICENSE.txt) +- cjson: MIT License (https://github.com/DaveGamble/cJSON/blob/master/LICENSE) +- zlib: zlib License (https://www.zlib.net/zlib_license.html) +- brotli: MIT License (https://github.com/google/brotli/blob/master/LICENSE) +- nghttp2: MIT License (https://github.com/nghttp2/nghttp2/blob/master/COPYING) +`; \ No newline at end of file diff --git a/client/javascript/main.js b/client/javascript/main.js index 48644b7..387fd6f 100644 --- a/client/javascript/main.js +++ b/client/javascript/main.js @@ -107,17 +107,6 @@ function perform_request(url, params, js_data_callback, js_end_callback, js_head return http_handle; } -function merge_arrays(arrays) { - let total_len = arrays.reduce((acc, val) => acc + val.length, 0); - let new_array = new Uint8Array(total_len); - let offset = 0; - for (let array of arrays) { - new_array.set(array, offset); - offset += array.length; - } - return new_array; -} - function create_response(response_data, response_info) { response_info.ok = response_info.status >= 200 && response_info.status < 300; response_info.statusText = status_messages[response_info.status] || ""; @@ -151,23 +140,31 @@ function create_response(response_data, response_info) { return response_obj; } - async function create_options(params) { let body = null; - if (params.body) { - body = await data_to_array(params.body); - params.body = true; + let request_obj = new Request("/", params); + let array_buffer = await request_obj.arrayBuffer(); + if (array_buffer.byteLength > 0) { + body = new Uint8Array(array_buffer); } + + let headers = params.headers || {}; + if (params.headers instanceof Headers) { + for(let [key, value] of headers) { + headers[key] = value; + } + } + params.headers = new HeadersDict(headers); - if (!params.headers) params.headers = {}; - params.headers = new HeadersDict(params.headers); - - if (params.referer) { - params.headers["Referer"] = params.referer; + if (params.referrer) { + params.headers["Referer"] = params.referrer; } if (!params.headers["User-Agent"]) { params.headers["User-Agent"] = navigator.userAgent; } + if (body) { + params.headers["Content-Type"] = request_obj.headers.get("Content-Type"); + } return body; } diff --git a/client/javascript/util.js b/client/javascript/util.js index f5db34a..6f0fff5 100644 --- a/client/javascript/util.js +++ b/client/javascript/util.js @@ -39,18 +39,13 @@ function get_error_str(error_code) { return UTF8ToString(error_ptr); } -//convert any data to a uint8array -async function data_to_array(data) { +//convert various data types to a uint8array (blobs excluded) +function data_to_array(data) { let data_array = null; if (typeof data === "string") { data_array = new TextEncoder().encode(data); } - else if (data instanceof Blob) { - let array_buffer = await data.arrayBuffer(); - data_array = new Uint8Array(array_buffer); - } - //any typedarray else if (data instanceof ArrayBuffer) { //dataview objects @@ -67,16 +62,9 @@ async function data_to_array(data) { } } - else if (data instanceof ReadableStream) { - let chunks = []; - for await (let chunk of data) { - chunks.push(chunk); - } - data_array = merge_arrays(chunks); - } - else { throw "invalid data type to be sent"; } + return data_array; -} +} \ No newline at end of file diff --git a/client/javascript/ws_polyfill.js b/client/javascript/ws_polyfill.js index 1adc046..2f79795 100644 --- a/client/javascript/ws_polyfill.js +++ b/client/javascript/ws_polyfill.js @@ -73,7 +73,6 @@ class FakeWebSocket extends EventTarget { } send(data) { - let is_text = typeof data === "string"; if (this.status === this.CONNECTING) { throw new DOMException("websocket not ready yet"); } @@ -81,15 +80,18 @@ class FakeWebSocket extends EventTarget { return; } - (async () => { - if (is_text) { - this.socket.send(data); - } - else { - let data_array = await data_to_array(data); - this.send(data_array); - } - })(); + if (data instanceof Blob) { + (async () => { + let array_buffer = await data.arrayBuffer(); + this.socket.send(new Uint8Array(array_buffer)); + })(); + } + else if (typeof data === "string") { + this.socket.send(data); + } + else { + this.socket.send(data_to_array(data)); + } } close() {