From ae56b13a4d49dbf922b8a0fbb0d557bb63c1d72b Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 23 Feb 2024 18:32:19 +0200 Subject: [PATCH] fix(http): allow `User-Agent` header to be set (#983) * fix(http): allow `User-Agent` header to be set closes #966 * lint * fix build --- .changes/http-user-agent.md | 6 ++++++ plugins/http/guest-js/index.ts | 21 ++++++++++++++++++--- plugins/http/src/api-iife.js | 2 +- plugins/http/src/commands.rs | 32 +++++++++++++++++++++++++++----- 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 .changes/http-user-agent.md diff --git a/.changes/http-user-agent.md b/.changes/http-user-agent.md new file mode 100644 index 00000000..b323659d --- /dev/null +++ b/.changes/http-user-agent.md @@ -0,0 +1,6 @@ +--- +"http": "patch" +"http-js": "patch" +--- + +Allow `User-Agent` header to be set. diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts index bba654f1..a1a6d77a 100644 --- a/plugins/http/guest-js/index.ts +++ b/plugins/http/guest-js/index.ts @@ -115,6 +115,21 @@ export async function fetch( const signal = init?.signal; + 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: [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(), + ]); + const req = new Request(input, init); const buffer = await req.arrayBuffer(); const reqData = buffer.byteLength ? Array.from(new Uint8Array(buffer)) : null; @@ -123,7 +138,7 @@ export async function fetch( clientConfig: { method: req.method, url: req.url, - headers: Array.from(req.headers.entries()), + headers: mappedHeaders, data: reqData, maxRedirections, connectTimeout, @@ -149,7 +164,7 @@ export async function fetch( status, statusText, url, - headers, + headers: responseHeaders, rid: responseRid, } = await invoke("plugin:http|fetch_send", { rid, @@ -169,7 +184,7 @@ export async function fetch( ? new Uint8Array(body) : null, { - headers, + headers: responseHeaders, status, statusText, }, diff --git a/plugins/http/src/api-iife.js b/plugins/http/src/api-iife.js index 22311a7c..40a09cb9 100644 --- a/plugins/http/src/api-iife.js +++ b/plugins/http/src/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},n){return window.__TAURI_INTERNALS__.invoke(e,t,n)}return"function"==typeof SuppressedError&&SuppressedError,e.fetch=async function(e,n){const r=n?.maxRedirections,i=n?.connectTimeout,a=n?.proxy;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy);const o=n?.signal,s=new Request(e,n),u=await s.arrayBuffer(),c=u.byteLength?Array.from(new Uint8Array(u)):null,d=await t("plugin:http|fetch",{clientConfig:{method:s.method,url:s.url,headers:Array.from(s.headers.entries()),data:c,maxRedirections:r,connectTimeout:i,proxy:a}});o?.addEventListener("abort",(()=>{t("plugin:http|fetch_cancel",{rid:d})}));const{status:_,statusText:f,url:l,headers:p,rid:h}=await t("plugin:http|fetch_send",{rid:d}),y=await t("plugin:http|fetch_read_body",{rid:h}),T=new Response(y instanceof ArrayBuffer&&y.byteLength?y:y instanceof Array&&y.length?new Uint8Array(y):null,{headers:p,status:_,statusText:f});return Object.defineProperty(T,"url",{value:l}),T},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,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=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&&T.byteLength?T:T instanceof Array&&T.length?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__})} diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs index 3831b9ab..d4b2469b 100644 --- a/plugins/http/src/commands.rs +++ b/plugins/http/src/commands.rs @@ -192,11 +192,33 @@ pub async fn fetch( let mut request = builder.build()?.request(method.clone(), url); - for (key, value) in &headers { - let name = HeaderName::from_bytes(key.as_bytes())?; - let v = HeaderValue::from_bytes(value.as_bytes())?; - if !matches!(name, header::HOST | header::CONTENT_LENGTH) { - request = request.header(name, v); + for (name, value) in &headers { + let name = HeaderName::from_bytes(name.as_bytes())?; + let value = HeaderValue::from_bytes(value.as_bytes())?; + if !matches!( + name, + // forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers + header::ACCEPT_CHARSET + | header::ACCEPT_ENCODING + | header::ACCESS_CONTROL_REQUEST_HEADERS + | header::ACCESS_CONTROL_REQUEST_METHOD + | header::CONNECTION + | header::CONTENT_LENGTH + | header::COOKIE + | header::DATE + | header::DNT + | header::EXPECT + | header::HOST + | header::ORIGIN + | header::REFERER + | header::SET_COOKIE + | header::TE + | header::TRAILER + | header::TRANSFER_ENCODING + | header::UPGRADE + | header::VIA + ) { + request = request.header(name, value); } }