From 43c6fd534fb4dc1e40637a37b22d7d1facce9fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Cruz?= Date: Wed, 18 Jan 2023 12:16:15 +0000 Subject: [PATCH] parse form data locally in specific cases In order to work around an EW bug, use local form data parsing temporarly when receiving media. --- backend/src/utils/body.ts | 61 ++++++++++++++++++++++----------------- functions/api/v2/media.ts | 18 +++++++++++- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/backend/src/utils/body.ts b/backend/src/utils/body.ts index 1bd134f..95986cf 100644 --- a/backend/src/utils/body.ts +++ b/backend/src/utils/body.ts @@ -2,6 +2,7 @@ // can be url encoded, form data or JSON. However, not working for formData // containing binary data (like File). export async function readBody(request: Request): Promise { + let form = null const contentType = request.headers.get('content-type') if (contentType === null) { throw new Error('invalid request') @@ -13,35 +14,41 @@ export async function readBody(request: Request): Promise { contentType.includes('multipart/form-data') && contentType.includes('boundary') ) { - console.log('will attempt local parse of form data') - const rBody = await request.text() - const enc = new TextEncoder() - const bodyArr = enc.encode(rBody) - const boundary = getBoundary(contentType) - console.log(`Got boundary ${boundary}`) - const parts = parse(bodyArr, boundary) - console.log(`parsed ${parts.length} parts`) - const dec = new TextDecoder() - const form: FormData = new FormData() - for (const part of parts) { - const value = dec.decode(part.data) - form.append(part.name || 'null', value) - } - - const out: any = {} - for (const [key, value] of form) { - out[key] = value - } - return out as T + form = await localFormDataParse(request) } else { - const form = await request.formData() - const out: any = {} - - for (const [key, value] of form) { - out[key] = value - } - return out as T + form = await request.formData() } + + const out: any = {} + + for (const [key, value] of form) { + out[key] = value + } + return out as T +} + +export async function localFormDataParse(request: Request): Promise { + const contentType = request.headers.get('content-type') + if (contentType === null) { + throw new Error('invalid request') + } + + console.log('will attempt local parse of form data') + const rBody = await request.text() + const enc = new TextEncoder() + const bodyArr = enc.encode(rBody) + const boundary = getBoundary(contentType) + console.log(`Got boundary ${boundary}`) + const parts = parse(bodyArr, boundary) + console.log(`parsed ${parts.length} parts`) + const dec = new TextDecoder() + const form: FormData = new FormData() + for (const part of parts) { + const value = dec.decode(part.data) + form.append(part.name || 'null', value) + } + + return form } // temporary code to deal with EW bug diff --git a/functions/api/v2/media.ts b/functions/api/v2/media.ts index f87f799..98cc604 100644 --- a/functions/api/v2/media.ts +++ b/functions/api/v2/media.ts @@ -5,6 +5,7 @@ import * as media from 'wildebeest/backend/src/media/image' import type { ContextData } from 'wildebeest/backend/src/types/context' import type { MediaAttachment } from 'wildebeest/backend/src/types/media' import type { Person } from 'wildebeest/backend/src/activitypub/actors' +import { localFormDataParse } from 'wildebeest/backend/src/utils/body' export const onRequest: PagesFunction = async ({ request, env, data }) => { return handleRequest(request, env.DATABASE, data.connectedActor, env.CF_ACCOUNT_ID, env.CF_API_TOKEN) @@ -18,7 +19,22 @@ export async function handleRequest( accountId: string, apiToken: string ): Promise { - const formData = await request.formData() + const contentType = request.headers.get('content-type') + if (contentType === null) { + throw new Error('invalid request') + } + + let formData = null + + if ( + contentType.includes('charset') && + contentType.includes('multipart/form-data') && + contentType.includes('boundary') + ) { + formData = await localFormDataParse(request) + } else { + formData = await request.formData() + } const domain = new URL(request.url).hostname if (!formData.has('file')) {