From 3e50ce65a721ee6a8f930613dd196f92a9126c1d Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Sat, 3 Aug 2024 20:24:25 +0300 Subject: [PATCH] fix: fetchNoCors and http_request data handling (#664) --- backend/decky_loader/utilities.py | 12 +++++++----- frontend/src/plugin-loader.tsx | 16 ++++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/backend/decky_loader/utilities.py b/backend/decky_loader/utilities.py index f7f22517d..a90244a1b 100644 --- a/backend/decky_loader/utilities.py +++ b/backend/decky_loader/utilities.py @@ -141,10 +141,10 @@ async def uninstall_plugin(self, name: str): # Loosely based on https://gist.github.com/mosquito/4dbfacd51e751827cda7ec9761273e95#file-proxy-py async def http_request(self, req: Request) -> StreamResponse: - if req.headers.get('X-Decky-Auth', '') != helpers.get_csrf_token() and req.query.get('auth', '') != helpers.get_csrf_token(): + if req.query['auth'] != helpers.get_csrf_token(): return Response(text='Forbidden', status=403) - url = req.headers["X-Decky-Fetch-URL"] if "X-Decky-Fetch-URL" in req.headers else unquote(req.query.get('fetch_url', '')) + url = unquote(req.query['fetch_url']) self.logger.info(f"Preparing {req.method} request to {url}") headers = dict(req.headers) @@ -180,7 +180,11 @@ async def http_request(self, req: Request) -> StreamResponse: body = await req.read() # TODO can this also be streamed? - async with ClientSession() as web: + # We disable auto-decompress so that the body is completely forwarded to the + # JS engine for it to do the decompression. Otherwise we need need to clear + # the Content-Encoding header in the response headers, however that would + # defeat the point of this proxy. + async with ClientSession(auto_decompress=False) as web: async with web.request(req.method, url, headers=headers, data=body, ssl=helpers.get_ssl_context()) as web_res: res = StreamResponse(headers=web_res.headers, status=web_res.status) if web_res.headers.get('Transfer-Encoding', '').lower() == 'chunked': @@ -190,8 +194,6 @@ async def http_request(self, req: Request) -> StreamResponse: self.logger.debug(f"Starting stream for {url}") async for data in web_res.content.iter_any(): await res.write(data) - if data: - await res.drain() self.logger.debug(f"Finished stream for {url}") return res diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index 6b954d69c..f7d362a7b 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -528,18 +528,22 @@ class PluginLoader extends Logger { // Same syntax as fetch but only supports the url-based syntax and an object for headers since it's the most common usage pattern fetchNoCors(input: string, init?: DeckyRequestInit | undefined): Promise { - const headers: { [name: string]: string } = { - ...(init?.headers as { [name: string]: string }), - 'X-Decky-Auth': deckyAuthToken, - 'X-Decky-Fetch-URL': input, + const { headers: initHeaders = {}, ...restOfInit } = init || {}; + const getPrefixedHeaders = () => { + let prefixedInitHeaders: { [name: string]: any } = {}; + for (const [key, value] of Object.entries(initHeaders)) { + prefixedInitHeaders[`X-Decky-Header-${key}`] = value; + } + return prefixedInitHeaders; }; + const headers: { [name: string]: string } = getPrefixedHeaders(); if (init?.excludedHeaders) { headers['X-Decky-Fetch-Excluded-Headers'] = init.excludedHeaders.join(', '); } - return fetch('http://127.0.0.1:1337/fetch', { - ...init, + return fetch(this.getExternalResourceURL(input), { + ...restOfInit, credentials: 'include', headers, });