oak icon indicating copy to clipboard operation
oak copied to clipboard

multipart/form-data response is missing boundary parameter

Open mfulton26 opened this issue 3 years ago • 4 comments

Discovered while answering multipart - Use FormData in an HTTP Response in Deno/Oak - Stack Overflow:

It appears Oak's automatic response body handling does not correctly handle FormData like I originally thought. The Content-Type response header is correctly inferred to be multipart/form-data but provides no boundary parameter. From RFC 7578, Appendix A:

"boundary" is now a required parameter in Content-Type

Clients receive the response fine without the boundary parameter but some have issues using it. e.g Insomnia's "Visual Preview" shows "Failed to parse multipart response: content-type missing boundary" (its "Source Code" and "Raw Data" views correctly show the response body though).


Here is an example using Oak which reproduces the issue:

import { Application } from "https://deno.land/x/[email protected]/mod.ts";

const app = new Application();

app.use((ctx) => {
  const data = new FormData();
  data.append("string1", "Hi");
  data.append("string2", "Bye");
  data.append("blob1", new Blob(['<a id="a">Hi</a>'], { type: "text/xml" }));
  data.append("blob2", new Blob(['<b id="b">Bye</b>'], { type: "text/xml" }));
  ctx.response.body = data;
});

await app.listen({ port: 8000 });
% curl --head localhost:8000
HTTP/1.1 200 OK
content-type: multipart/form-data
date: Sat, 11 Sep 2021 12:40:37 GMT

Here is an example using native Deno HTTP serving where it correctly sets the boundary parameter in the Content-Type response header when responding with FormData:

const server = Deno.listen({ port: 8000 });

for await (const conn of server) serveHttp(conn);

async function serveHttp(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const requestEvent of httpConn) {
    const data = new FormData();
    data.append("string1", "Hi");
    data.append("string2", "Bye");
    data.append("blob1", new Blob(['<a id="a">Hi</a>'], { type: "text/xml" }));
    data.append("blob2", new Blob(['<b id="b">Bye</b>'], { type: "text/xml" }));
    requestEvent.respondWith(new Response(data));
  }
}
% curl --head localhost:8000
HTTP/1.1 200 OK
content-type: multipart/form-data; boundary=----9980216681407476676035360426
date: Sat, 11 Sep 2021 12:43:54 GMT

mfulton26 avatar Sep 11 '21 12:09 mfulton26

A client browser couldn't use the fetch API to get the response of a FormData() from the Oak server, as it returns a TypeError: Could not parse content as FormData.

 fetch('myRoute/getFormData', { 
    method: 'POST', 
  })
  .then(response => response.formData()) // TypeError
  .then(res => { console.log(res); });

It seems that browsers are incapable of parsing the response as a FormData because of the the missing boundary.

In the Oak server I have added the Content-Type header for multipart:

context.response.headers.set('Content-Type', 'multipart/form-data');

But the result is the same. Maybe the boundary parameter can be added manually? Something like this, I don't know:

context.response.headers.set('Content-Type', 'multipart/form-data; boundary=--something');

SathoriStudio avatar Sep 11 '21 20:09 SathoriStudio

It looks like fetch is able to successfully parse content as FormData from the Deno.serveHttp example I included above so it seems that Oak not sending the boundary parameter causes issues for more than just Insomnia but web browsers too. Great catch @SathoriStudio.

mfulton26 avatar Sep 12 '21 17:09 mfulton26

Thanks for the investigation. I will look into fixing this.

kitsonk avatar Sep 17 '21 01:09 kitsonk

Hi, is it possible to send the FormData from Deno/Oak to the web client?

In Firefox I receive the error: TypeError: Could not parse content as FormData and in Chrome it says: TypeError: Invalid MIME type when I try to receive the response using response.formData().

It seems that sending the FormData from the server to the client is complicated, but not the other way around.

M3cubo avatar Aug 28 '22 11:08 M3cubo

I have created a little repo that contains the code examples provided here: https://github.com/M3cubo/Oak-Form-Data

M3cubo avatar Mar 17 '23 17:03 M3cubo

Hi all. Are there any plans to fix this soon? Thanks!

harvestcore avatar Jan 18 '24 12:01 harvestcore

The error when parsing in the client the formData still appears when I use the oak version, but the basic Deno version works fine: https://github.com/M3cubo/Oak-Form-Data (I have tried with oak 13.0.0)

It still shows the error "Uncaught (in promise) TypeError: Could not parse content as FormData. " in Firefox, and "Uncaught (in promise) TypeError: Invalid MIME type" in Chrome.

M3cubo avatar Feb 03 '24 18:02 M3cubo

This hasn't been published yet in a release.

kitsonk avatar Feb 04 '24 00:02 kitsonk