From 850773cbcff6f80ef992d9fe29f1e2d03b661a8c Mon Sep 17 00:00:00 2001 From: Nick K Date: Fri, 2 Sep 2022 18:31:42 +0300 Subject: [PATCH] Support FormData without known length (#2120) --- package.json | 2 +- source/core/index.ts | 4 +++- test/helpers/create-https-test-server.ts | 7 +++--- test/https.ts | 7 +++--- test/post.ts | 30 ++++++++++++++++++++++++ test/types/pem.ts | 7 ++++++ 6 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 test/types/pem.ts diff --git a/package.json b/package.json index e3b47b9fc..8e0260301 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "cacheable-lookup": "^6.0.4", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", - "form-data-encoder": "^2.0.1", + "form-data-encoder": "^2.1.0", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", diff --git a/source/core/index.ts b/source/core/index.ts index 4ead97ac5..69dcb151e 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -574,7 +574,9 @@ export default class Request extends Duplex implements RequestEvents { headers['content-type'] = encoder.headers['Content-Type']; } - headers['content-length'] = encoder.headers['Content-Length']; + if ('Content-Length' in encoder.headers) { + headers['content-length'] = encoder.headers['Content-Length']; + } options.body = encoder.encode(); } diff --git a/test/helpers/create-https-test-server.ts b/test/helpers/create-https-test-server.ts index 05b5c3565..3ea6a66b2 100644 --- a/test/helpers/create-https-test-server.ts +++ b/test/helpers/create-https-test-server.ts @@ -5,6 +5,7 @@ import type {SecureContextOptions} from 'tls'; import express from 'express'; import pify from 'pify'; import pem from 'pem'; +import type {CreateCsr, CreateCertificate} from '../types/pem.js'; export type HttpsServerOptions = { commonName?: string; @@ -25,8 +26,8 @@ export type ExtendedHttpsTestServer = { } & express.Express; const createHttpsTestServer = async (options: HttpsServerOptions = {}): Promise => { - const createCsr = pify(pem.createCSR); - const createCertificate = pify(pem.createCertificate); + const createCsr = pify(pem.createCSR as CreateCsr); + const createCertificate = pify(pem.createCertificate as CreateCertificate); const caCsrResult = await createCsr({commonName: 'authority'}); const caResult = await createCertificate({ @@ -68,7 +69,7 @@ const createHttpsTestServer = async (options: HttpsServerOptions = {}): Promise< await pify(server.https.listen.bind(server.https))(); - server.caKey = caKey; + server.caKey = caKey as any; server.caCert = caCert; server.port = (server.https.address() as net.AddressInfo).port; server.url = `https://localhost:${(server.port)}`; diff --git a/test/https.ts b/test/https.ts index e1b80ec75..8ddd2c61a 100644 --- a/test/https.ts +++ b/test/https.ts @@ -6,10 +6,11 @@ import pify from 'pify'; import pem from 'pem'; import got from '../source/index.js'; import {withHttpsServer} from './helpers/with-server.js'; +import type {CreatePrivateKey, CreateCsr, CreateCertificate} from './types/pem.js'; -const createPrivateKey = pify(pem.createPrivateKey); -const createCsr = pify(pem.createCSR); -const createCertificate = pify(pem.createCertificate); +const createPrivateKey = pify(pem.createPrivateKey as CreatePrivateKey); +const createCsr = pify(pem.createCSR as CreateCsr); +const createCertificate = pify(pem.createCertificate as CreateCertificate); const createPkcs12 = pify(pem.createPkcs12); test('https request without ca', withHttpsServer(), async (t, server, got) => { diff --git a/test/post.ts b/test/post.ts index a19614c52..96a8960c2 100644 --- a/test/post.ts +++ b/test/post.ts @@ -361,6 +361,36 @@ test('body - sends files with spec-compliant FormData', withServer, async (t, se t.deepEqual(body, expected); }); +test('body - sends form-data with without known length', withServer, async (t, server, got) => { + server.post('/', echoMultipartBody); + const fullPath = path.resolve('test/fixtures/ok'); + + function getFileStream() { + const fileStream = fs.createReadStream(fullPath); + const passThrough = new stream.PassThrough(); + fileStream.pipe(passThrough); + return passThrough; + } + + const expected = { + file: await fsPromises.readFile(fullPath, 'utf8'), + }; + + const form = new FormDataNode(); + form.set('file', { + [Symbol.toStringTag]: 'File', + type: 'text/plain', + name: 'file.txt', + stream() { + return getFileStream(); + }, + }); + + const body = await got.post({body: form}).json(); + + t.deepEqual(body, expected); +}); + test('throws on upload error', withServer, async (t, server, got) => { server.post('/', defaultEndpoint); diff --git a/test/types/pem.ts b/test/types/pem.ts new file mode 100644 index 000000000..58fa3e650 --- /dev/null +++ b/test/types/pem.ts @@ -0,0 +1,7 @@ +import type {CertificateCreationOptions, CertificateCreationResult, PrivateKeyCreationOptions, CSRCreationOptions, Callback} from 'pem'; + +export type CreateCertificate = (options: CertificateCreationOptions, callback: Callback) => void; + +export type CreateCsr = (options: CSRCreationOptions, callback: Callback<{csr: string; clientKey: string}>) => void; + +export type CreatePrivateKey = (keyBitsize: number, options: PrivateKeyCreationOptions, callback: Callback<{key: string}>) => void;