From 4e038142da15af7647ed0cde77b568bfe6b5268e Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Tue, 18 Feb 2025 16:55:31 +0100 Subject: [PATCH] superagent: fix multipart form-data --- src/superagent.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/superagent.js b/src/superagent.js index 37743245a..e29822672 100644 --- a/src/superagent.js +++ b/src/superagent.js @@ -82,7 +82,7 @@ class Request { if (error) reject(error); else resolve(result); }); - if (this.body) request.write(this.body, 'utf8'); + if (this.body) request.write(this.body); request.end(); }); @@ -134,11 +134,11 @@ class Request { const contentType = this.options.headers['content-type']; if (!contentType || contentType === 'application/json') { this.options.headers['content-type'] = 'application/json'; - this.body = JSON.stringify(data); - this.options.headers['content-length'] = Buffer.byteLength(this.body, 'utf8'); + this.body = Buffer.from(JSON.stringify(data), 'utf8'); + this.options.headers['content-length'] = this.body.byteLength; } else if (contentType === 'application/x-www-form-urlencoded') { - this.body = (new URLSearchParams(data)).toString(); - this.options.headers['content-length'] = Buffer.byteLength(this.body, 'utf8'); + this.body = Buffer.from((new URLSearchParams(data)).toString(), 'utf8'); + this.options.headers['content-length'] = this.body.byteLength; } return this; } @@ -169,11 +169,17 @@ class Request { return this; } - attach(name, filepath) { - if (!(this.options.body instanceof FormData)) this.options.body = new FormData(); - const data = fs.readFileSync(filepath); - this.options.body.append(name, new Blob([data]), path.basename(filepath)); - delete this.options.headers['content-type']; // explicitly remove it. without it boundary won't make it to the header! + attach(name, filepath) { // this is only used in tests and thus simplistic + const boundary = '----WebKitFormBoundary' + Math.random().toString(36).substring(2); + + const partHeader = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${name}" filename="${path.basename(filepath)}"\r\n\r\n`, 'utf8'); + const partData = fs.readFileSync(filepath); + const partTrailer = Buffer.from(`\r\n--${boundary}--\r\n`, 'utf8'); + this.body = Buffer.concat([partHeader, partData, partTrailer]); + + this.options.headers['content-type'] = `multipart/form-data; boundary=${boundary}`; + this.options.headers['content-length'] = this.body.byteLength; + return this; }