Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove third party blob support #2727

Merged
merged 4 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 2 additions & 108 deletions lib/web/fetch/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,107 +93,6 @@ class File extends Blob {
}
}

class FileLike {
constructor (blobLike, fileName, options = {}) {
// TODO: argument idl type check

// The File constructor is invoked with two or three parameters, depending
// on whether the optional dictionary parameter is used. When the File()
// constructor is invoked, user agents must run the following steps:

// 1. Let bytes be the result of processing blob parts given fileBits and
// options.

// 2. Let n be the fileName argument to the constructor.
const n = fileName

// 3. Process FilePropertyBag dictionary argument by running the following
// substeps:

// 1. If the type member is provided and is not the empty string, let t
// be set to the type dictionary member. If t contains any characters
// outside the range U+0020 to U+007E, then set t to the empty string
// and return from these substeps.
// TODO
const t = options.type

// 2. Convert every character in t to ASCII lowercase.
// TODO

// 3. If the lastModified member is provided, let d be set to the
// lastModified dictionary member. If it is not provided, set d to the
// current date and time represented as the number of milliseconds since
// the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
const d = options.lastModified ?? Date.now()

// 4. Return a new File object F such that:
// F refers to the bytes byte sequence.
// F.size is set to the number of total bytes in bytes.
// F.name is set to n.
// F.type is set to t.
// F.lastModified is set to d.

this[kState] = {
blobLike,
name: n,
type: t,
lastModified: d
}
}

stream (...args) {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.stream(...args)
}

arrayBuffer (...args) {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.arrayBuffer(...args)
}

slice (...args) {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.slice(...args)
}

text (...args) {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.text(...args)
}

get size () {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.size
}

get type () {
webidl.brandCheck(this, FileLike)

return this[kState].blobLike.type
}

get name () {
webidl.brandCheck(this, FileLike)

return this[kState].name
}

get lastModified () {
webidl.brandCheck(this, FileLike)

return this[kState].lastModified
}

get [Symbol.toStringTag] () {
return 'File'
}
}

Object.defineProperties(File.prototype, {
[Symbol.toStringTag]: {
value: 'File',
Expand Down Expand Up @@ -326,13 +225,8 @@ function convertLineEndingsNative (s) {
function isFileLike (object) {
return (
(NativeFile && object instanceof NativeFile) ||
object instanceof File || (
object &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
object[Symbol.toStringTag] === 'File'
)
object instanceof File // undici File
)
}

module.exports = { File, FileLike, isFileLike }
module.exports = { File, isFileLike }
10 changes: 3 additions & 7 deletions lib/web/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const { isBlobLike, iteratorMixin } = require('./util')
const { kState } = require('./symbols')
const { kEnumerableProperty } = require('../../core/util')
const { File: UndiciFile, FileLike, isFileLike } = require('./file')
const { File: UndiciFile, isFileLike } = require('./file')
const { webidl } = require('./webidl')
const { File: NativeFile } = require('node:buffer')

Expand Down Expand Up @@ -192,9 +192,7 @@ function makeEntry (name, value, filename) {
// 1. If value is not a File object, then set value to a new File object,
// representing the same bytes, whose name attribute value is "blob"
if (!isFileLike(value)) {
value = value instanceof Blob
? new File([value], 'blob', { type: value.type })
: new FileLike(value, 'blob', { type: value.type })
value = new File([value], 'blob', { type: value.type })
}

// 2. If filename is given, then set value to a new File object,
Expand All @@ -206,9 +204,7 @@ function makeEntry (name, value, filename) {
lastModified: value.lastModified
}

value = (NativeFile && value instanceof NativeFile) || value instanceof UndiciFile
? new File([value], filename, options)
: new FileLike(value, filename, options)
value = new File([value], filename, options)
}
}

Expand Down
36 changes: 3 additions & 33 deletions test/fetch/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ const { Blob } = require('node:buffer')
const { test } = require('node:test')
const assert = require('node:assert')
const { tspl } = require('@matteo.collina/tspl')
const { File, FileLike } = require('../../lib/web/fetch/file')
const { File } = require('../../lib/web/fetch/file')

test('args validation', (t) => {
const { throws, doesNotThrow, strictEqual } = tspl(t, { plan: 14 })
const { throws, doesNotThrow, strictEqual } = tspl(t, { plan: 4 })

throws(() => {
File.prototype.name.toString()
Expand All @@ -19,36 +19,7 @@ test('args validation', (t) => {
File.prototype[Symbol.toStringTag].charAt(0)
}, TypeError)

throws(() => {
FileLike.prototype.stream.call(null)
}, TypeError)
throws(() => {
FileLike.prototype.arrayBuffer.call(null)
}, TypeError)
throws(() => {
FileLike.prototype.slice.call(null)
}, TypeError)
throws(() => {
FileLike.prototype.text.call(null)
}, TypeError)
throws(() => {
FileLike.prototype.size.toString()
}, TypeError)
throws(() => {
FileLike.prototype.type.toString()
}, TypeError)
throws(() => {
FileLike.prototype.name.toString()
}, TypeError)
throws(() => {
FileLike.prototype.lastModified.toString()
}, TypeError)
doesNotThrow(() => {
FileLike.prototype[Symbol.toStringTag].charAt(0)
}, TypeError)

strictEqual(File.prototype[Symbol.toStringTag], 'File')
strictEqual(FileLike.prototype[Symbol.toStringTag], 'File')
})

test('return value of File.lastModified', (t) => {
Expand All @@ -61,9 +32,8 @@ test('return value of File.lastModified', (t) => {
})

test('Symbol.toStringTag', (t) => {
const { strictEqual } = tspl(t, { plan: 2 })
const { strictEqual } = tspl(t, { plan: 1 })
strictEqual(new File([], '')[Symbol.toStringTag], 'File')
strictEqual(new FileLike()[Symbol.toStringTag], 'File')
})

test('arguments', () => {
Expand Down
12 changes: 0 additions & 12 deletions test/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const { test } = require('node:test')
const assert = require('node:assert')
const { tspl } = require('@matteo.collina/tspl')
const { FormData, File, Response } = require('../../')
const { Blob: ThirdPartyBlob } = require('formdata-node')
const { Blob } = require('node:buffer')
const { isFormDataLike } = require('../../lib/core/util')
const ThirdPartyFormDataInvalid = require('form-data')
Expand Down Expand Up @@ -110,17 +109,6 @@ test('append blob', async () => {
assert.strictEqual(form.get('asd'), null)
})

test('append third-party blob', async () => {
const form = new FormData()
form.set('asd', new ThirdPartyBlob(['asd1'], { type: 'text/plain' }))

assert.strictEqual(form.has('asd'), true)
assert.strictEqual(form.get('asd').type, 'text/plain')
assert.strictEqual(await form.get('asd').text(), 'asd1')
form.delete('asd')
assert.strictEqual(form.get('asd'), null)
})

test('append string', () => {
const form = new FormData()
form.set('k1', 'v1')
Expand Down
9 changes: 0 additions & 9 deletions test/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const {
} = require('../../')
const { fromInnerRequest, makeRequest } = require('../../lib/web/fetch/request')
const {
Blob: ThirdPartyBlob,
FormData: ThirdPartyFormData
} = require('formdata-node')
const { kState, kGuard, kRealm, kSignal, kHeaders } = require('../../lib/web/fetch/symbols')
Expand Down Expand Up @@ -419,14 +418,6 @@ test('RequestInit.signal option', async () => {
}), TypeError)
})

test('constructing Request with third party Blob body', async () => {
const blob = new ThirdPartyBlob(['text'])
const req = new Request('http://asd', {
method: 'POST',
body: blob
})
assert.strictEqual(await req.text(), 'text')
})
test('constructing Request with third party FormData body', async () => {
const form = new ThirdPartyFormData()
form.set('key', 'value')
Expand Down
6 changes: 0 additions & 6 deletions test/fetch/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const {
} = require('../../')
const { fromInnerResponse, makeResponse } = require('../../lib/web/fetch/response')
const {
Blob: ThirdPartyBlob,
FormData: ThirdPartyFormData
} = require('formdata-node')
const { kState, kGuard, kRealm, kHeaders } = require('../../lib/web/fetch/symbols')
Expand Down Expand Up @@ -222,11 +221,6 @@ test('constructing a Response with a ReadableStream body', async (t) => {
})
})

test('constructing Response with third party Blob body', async () => {
const blob = new ThirdPartyBlob(['text'])
const res = new Response(blob)
assert.strictEqual(await res.text(), 'text')
})
test('constructing Response with third party FormData body', async () => {
const form = new ThirdPartyFormData()
form.set('key', 'value')
Expand Down
34 changes: 6 additions & 28 deletions test/issue-2065.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { tspl } = require('@matteo.collina/tspl')
const { test, after } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { createReadStream } = require('node:fs')
const { openAsBlob } = require('node:fs')
const { File, FormData, request } = require('..')

test('undici.request with a FormData body should set content-length header', async (t) => {
Expand All @@ -27,41 +27,19 @@ test('undici.request with a FormData body should set content-length header', asy
})
})

test('undici.request with a FormData stream value should set transfer-encoding header', async (t) => {
t = tspl(t, { plan: 1 })
test('undici.request with a FormData stream value should set transfer-encoding header', { skip: !openAsBlob }, async (t) => {
const { ok } = tspl(t, { plan: 1 })

const server = createServer((req, res) => {
t.strictEqual(req.headers['transfer-encoding'], 'chunked')
ok(req.headers['content-type'].startsWith('multipart/form-data'))
res.end()
}).listen(0)

after(() => server.close())
t.after(server.close.bind(server))
await once(server, 'listening')

class BlobFromStream {
#stream
#type
constructor (stream, type) {
this.#stream = stream
this.#type = type
}

stream () {
return this.#stream
}

get type () {
return this.#type
}

get [Symbol.toStringTag] () {
return 'Blob'
}
}

const body = new FormData()
const fileReadable = createReadStream(__filename)
body.set('file', new BlobFromStream(fileReadable, '.js'), 'streamfile')
body.set('file', await openAsBlob(__filename), 'streamfile')

await request(`http://localhost:${server.address().port}`, {
method: 'POST',
Expand Down
Loading