From 9d7ae45b0edf9b22c73e7d7c413a784bb35c3d77 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 21 May 2024 13:43:26 +0300 Subject: [PATCH] fix(http): include browser headers if not set by user (#1354) * fix(http): include browser automatic headers if not set by user close #1349 * fmt * Update browser-headers.md * lint * generate api --- .changes/browser-headers.md | 5 ++++ plugins/http/api-iife.js | 2 +- plugins/http/guest-js/index.ts | 43 +++++++++++++++++++++++----------- plugins/http/src/commands.rs | 10 ++++---- 4 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 .changes/browser-headers.md diff --git a/.changes/browser-headers.md b/.changes/browser-headers.md new file mode 100644 index 00000000..c7b3cb71 --- /dev/null +++ b/.changes/browser-headers.md @@ -0,0 +1,5 @@ +--- +"http-js": "patch" +--- + +Include headers created by browser if not declared by user, which fixes missing headers like `Content-Type` when using `FormData`. diff --git a/plugins/http/api-iife.js b/plugins/http/api-iife.js index 4509f055..55842c2c 100644 --- a/plugins/http/api-iife.js +++ b/plugins/http/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}return"function"==typeof SuppressedError&&SuppressedError,e.fetch=async function(e,r){const n=r?.maxRedirections,a=r?.connectTimeout,i=r?.proxy;r&&(delete r.maxRedirections,delete r.connectTimeout,delete r.proxy);const s=r?.signal,o=(r?.headers?r.headers instanceof Headers?Array.from(r.headers.entries()):Array.isArray(r.headers)?r.headers:Object.entries(r.headers):[]).map((([e,t])=>[e,"string"==typeof t?t:t.toString()])),d=new Request(e,r),c=await d.arrayBuffer(),u=0!==c.byteLength?Array.from(new Uint8Array(c)):null,_=await t("plugin:http|fetch",{clientConfig:{method:d.method,url:d.url,headers:o,data:u,maxRedirections:n,connectTimeout:a,proxy:i}});s?.addEventListener("abort",(()=>{t("plugin:http|fetch_cancel",{rid:_})}));const{status:f,statusText:h,url:p,headers:l,rid:y}=await t("plugin:http|fetch_send",{rid:_}),T=await t("plugin:http|fetch_read_body",{rid:y}),w=new Response(T instanceof ArrayBuffer&&0!==T.byteLength?T:T instanceof Array&&T.length>0?new Uint8Array(T):null,{headers:l,status:f,statusText:h});return Object.defineProperty(w,"url",{value:p}),w},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}return"function"==typeof SuppressedError&&SuppressedError,e.fetch=async function(e,r){const n=r?.maxRedirections,a=r?.connectTimeout,s=r?.proxy;r&&(delete r.maxRedirections,delete r.connectTimeout,delete r.proxy);const i=r?.signal,o=r?.headers?r.headers instanceof Headers?r.headers:new Headers(r.headers):new Headers,d=new Request(e,r),c=await d.arrayBuffer(),u=0!==c.byteLength?Array.from(new Uint8Array(c)):null;for(const[e,t]of d.headers)o.get(e)||o.set(e,t);const _=(o instanceof Headers?Array.from(o.entries()):Array.isArray(o)?o:Object.entries(o)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()])),f=await t("plugin:http|fetch",{clientConfig:{method:d.method,url:d.url,headers:_,data:u,maxRedirections:n,connectTimeout:a,proxy:s}});i?.addEventListener("abort",(()=>{t("plugin:http|fetch_cancel",{rid:f})}));const{status:h,statusText:p,url:l,headers:y,rid:w}=await t("plugin:http|fetch_send",{rid:f}),T=await t("plugin:http|fetch_read_body",{rid:w}),A=new Response(T instanceof ArrayBuffer&&0!==T.byteLength?T:T instanceof Array&&T.length>0?new Uint8Array(T):null,{headers:y,status:h,statusText:p});return Object.defineProperty(A,"url",{value:l}),A},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts index c7e135a7..bac18a70 100644 --- a/plugins/http/guest-js/index.ts +++ b/plugins/http/guest-js/index.ts @@ -117,30 +117,45 @@ export async function fetch( const headers = init?.headers ? init.headers instanceof Headers - ? Array.from(init.headers.entries()) - : Array.isArray(init.headers) - ? init.headers - : Object.entries(init.headers) - : []; - - const mappedHeaders: Array<[string, string]> = headers.map(([name, val]) => [ - name, - // we need to ensure we have all values as strings - // eslint-disable-next-line - typeof val === "string" ? val : (val as any).toString(), - ]); + ? init.headers + : new Headers(init.headers) + : new Headers(); const req = new Request(input, init); const buffer = await req.arrayBuffer(); - const reqData = + const data = buffer.byteLength !== 0 ? Array.from(new Uint8Array(buffer)) : null; + // append new headers created by the browser `Request` implementation, + // if not already declared by the caller of this function + for (const [key, value] of req.headers) { + if (!headers.get(key)) { + headers.set(key, value); + } + } + + const headersArray = + headers instanceof Headers + ? Array.from(headers.entries()) + : Array.isArray(headers) + ? headers + : Object.entries(headers); + + const mappedHeaders: Array<[string, string]> = headersArray.map( + ([name, val]) => [ + name, + // we need to ensure we have all header values as strings + // eslint-disable-next-line + typeof val === "string" ? val : (val as any).toString(), + ], + ); + const rid = await invoke("plugin:http|fetch", { clientConfig: { method: req.method, url: req.url, headers: mappedHeaders, - data: reqData, + data, maxRedirections, connectTimeout, proxy, diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs index 9bb07a00..4f89f1fe 100644 --- a/plugins/http/src/commands.rs +++ b/plugins/http/src/commands.rs @@ -45,7 +45,7 @@ pub struct FetchResponse { rid: ResourceId, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ClientConfig { method: String, @@ -57,7 +57,7 @@ pub struct ClientConfig { proxy: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Proxy { all: Option, @@ -65,7 +65,7 @@ pub struct Proxy { https: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(untagged)] pub enum UrlOrConfig { @@ -73,7 +73,7 @@ pub enum UrlOrConfig { Config(ProxyConfig), } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProxyConfig { url: String, @@ -81,7 +81,7 @@ pub struct ProxyConfig { no_proxy: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] pub struct BasicAuth { username: String, password: String,