Skip to content

Commit

Permalink
Properly handle multipart file uploads in the dev server (#4742)
Browse files Browse the repository at this point in the history
* Properly allow file uploads in the dev server

* Smaller image

* movethe test over
  • Loading branch information
matthewp authored Sep 14, 2022
1 parent dc05bc0 commit cf8a7e9
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-melons-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Allow file uploads in dev server
9 changes: 5 additions & 4 deletions packages/astro/src/vite-plugin-astro-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,14 @@ async function handleRequest(

let body: ArrayBuffer | undefined = undefined;
if (!(req.method === 'GET' || req.method === 'HEAD')) {
let bytes: string[] = [];
let bytes: Uint8Array[] = [];
await new Promise((resolve) => {
req.setEncoding('utf-8');
req.on('data', (bts) => bytes.push(bts));
req.on('data', part => {
bytes.push(part);
});
req.on('end', resolve);
});
body = new TextEncoder().encode(bytes.join('')).buffer;
body = Buffer.concat(bytes);
}

// Headers are only available when using SSR.
Expand Down
3 changes: 3 additions & 0 deletions packages/astro/test/api-routes.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
import * as fs from 'fs';
import { FormData, File } from 'node-fetch'

describe('API routes', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;

before(async () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/astro/test/fixtures/api-routes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"private": true,
"dependencies": {
"astro": "workspace:*"
},
"scripts": {
"dev": "astro dev"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import fs from 'node:fs';

export function get() {
return {
body: 'ok'
};
}

export async function post({ request }) {
const data = await request.formData();
const file = data.get('file');

if (file) {
const buffer = await file.arrayBuffer();
const realBuffer = await fs.promises.readFile(new URL('../images/penguin.jpg', import.meta.url));

if(buffersEqual(buffer, realBuffer)) {
return new Response('ok', { status: 200 });
}
}
return new Response(null, { status: 400 });
}

function buffersEqual(buf1, buf2) {
if (buf1.byteLength != buf2.byteLength) return false;
const dv1 = new Uint8Array(buf1);
const dv2 = new Uint8Array(buf2);
for (let i = 0; i !== buf1.byteLength; i++) {
if (dv1[i] != dv2[i]) return false;
}
return true;
}
15 changes: 15 additions & 0 deletions packages/astro/test/ssr-api-route.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
import testAdapter from './test-adapter.js';
import { FormData, File } from 'node-fetch';

describe('API routes in SSR', () => {
/** @type {import('./test-utils').Fixture} */
Expand Down Expand Up @@ -54,6 +55,20 @@ describe('API routes in SSR', () => {
expect(text).to.equal(`ok`);
});

it('Can be passed binary data from multipart formdata', async () => {
const formData = new FormData();
const raw = await fs.promises.readFile(new URL('./fixtures/ssr-api-route/src/images/penguin.jpg', import.meta.url));
const file = new File([raw], 'penguin.jpg', { type: 'text/jpg' });
formData.set('file', file, 'penguin.jpg');

const res = await fixture.fetch('/binary', {
method: 'POST',
body: formData
});

expect(res.status).to.equal(200);
});

it('Infer content type with charset for { body } shorthand', async () => {
const response = await fixture.fetch('/food.json', {
method: 'GET',
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ polyfill(globalThis, {
* @typedef {Object} Fixture
* @property {typeof build} build
* @property {(url: string) => string} resolveUrl
* @property {(url: string, opts: any) => Promise<Response>} fetch
* @property {(url: string, opts: Parameters<typeof fetch>[1]) => Promise<Response>} fetch
* @property {(path: string) => Promise<string>} readFile
* @property {(path: string, updater: (content: string) => string) => Promise<void>} writeFile
* @property {(path: string) => Promise<string[]>} readdir
Expand Down

0 comments on commit cf8a7e9

Please sign in to comment.