Skip to content

Commit

Permalink
feat: accept third-party Blobs in FormData (#1099)
Browse files Browse the repository at this point in the history
  • Loading branch information
wojpawlik committed Nov 17, 2021
1 parent 64d07c3 commit 46eff3f
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 7 deletions.
13 changes: 6 additions & 7 deletions lib/fetch/formdata.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
'use strict'

const { Blob } = require('buffer')
const { isBlobLike, toUSVString } = require('./util')
const { kState } = require('./symbols')
const { File } = require('./file')
const { toUSVString } = require('./util')

class FormData {
constructor (...args) {
Expand All @@ -25,7 +24,7 @@ class FormData {
`Failed to execute 'append' on 'FormData': 2 arguments required, but only ${args.length} present.`
)
}
if (args.length === 3 && !(args[1] instanceof Blob)) {
if (args.length === 3 && !isBlobLike(args[1])) {
throw new TypeError(
"Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'"
)
Expand All @@ -34,7 +33,7 @@ class FormData {
const filename = args.length === 3 ? toUSVString(args[2]) : undefined

// 1. Let value be value if given; otherwise blobValue.
const value = args[1] instanceof Blob ? args[1] : toUSVString(args[1])
const value = isBlobLike(args[1]) ? args[1] : toUSVString(args[1])

// 2. Let entry be the result of creating an entry with
// name, value, and filename if given.
Expand Down Expand Up @@ -135,7 +134,7 @@ class FormData {
`Failed to execute 'set' on 'FormData': 2 arguments required, but only ${args.length} present.`
)
}
if (args.length === 3 && !(args[1] instanceof Blob)) {
if (args.length === 3 && !isBlobLike(args[1])) {
throw new TypeError(
"Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'"
)
Expand All @@ -147,7 +146,7 @@ class FormData {
// are:

// 1. Let value be value if given; otherwise blobValue.
const value = args[1] instanceof Blob ? args[1] : toUSVString(args[1])
const value = isBlobLike(args[1]) ? args[1] : toUSVString(args[1])

// 2. Let entry be the result of creating an entry with name, value, and
// filename if given.
Expand Down Expand Up @@ -214,7 +213,7 @@ function makeEntry (name, value, filename) {

// 3. If value is a Blob object and not a File object, then set value to a new File
// object, representing the same bytes, whose name attribute value is "blob".
if (value instanceof Blob && !(value instanceof File)) {
if (isBlobLike(value) && !(value instanceof File)) {
value = new File([value], 'blob')
}

Expand Down
14 changes: 14 additions & 0 deletions lib/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const { redirectStatus } = require('./constants')
const { performance } = require('perf_hooks')
const { Blob } = require('buffer')
const nodeUtil = require('util')

let ReadableStream
Expand Down Expand Up @@ -68,6 +69,18 @@ function requestBadPort (request) {
return 'allowed'
}

// based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
function isBlobLike (object) {
return object instanceof Blob || (
object &&
typeof object === 'object' &&
typeof object.constructor === 'function' &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
/^(Blob|File)$/.test(object[Symbol.toStringTag])
)
}

// Check whether |statusText| is a ByteString and
// matches the Reason-Phrase token production.
// RFC 2616: https://tools.ietf.org/html/rfc2616
Expand Down Expand Up @@ -349,5 +362,6 @@ module.exports = {
requestCurrentURL,
responseURL,
responseLocationURL,
isBlobLike,
isValidReasonPhrase
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"cronometro": "^0.8.0",
"delay": "^5.0.0",
"docsify-cli": "^4.4.3",
"formdata-node": "^4.3.1",
"https-pem": "^2.0.0",
"husky": "^7.0.2",
"jest": "^27.2.0",
Expand Down
13 changes: 13 additions & 0 deletions test/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const { test } = require('tap')
const { FormData, File } = require('../../')
const { Blob: ThirdPartyBlob } = require('formdata-node')
const { Blob } = require('buffer')

test('arg validation', (t) => {
Expand Down Expand Up @@ -104,6 +105,18 @@ test('append blob', async (t) => {
t.end()
})

test('append third-party blob', async (t) => {
const form = new FormData()
form.set('asd', new ThirdPartyBlob(['asd1']))

t.equal(form.has('asd'), true)
t.equal(await form.get('asd').text(), 'asd1')
form.delete('asd')
t.equal(form.get('asd'), null)

t.end()
})

test('append string', (t) => {
const form = new FormData()
form.set('k1', 'v1')
Expand Down

0 comments on commit 46eff3f

Please sign in to comment.