Allow third party blob implementation (#629)

* Support making request with any blob that have stream() method
* don't clone blob when cloning request
* check for blob api that node-fetch uses
This commit is contained in:
David Frank 2019-05-01 11:44:27 +08:00 committed by GitHub
parent d8f5ba0e97
commit 0fc414c2a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 37 additions and 12 deletions

View File

@ -37,9 +37,8 @@ export default function Body(body, {
} else if (isURLSearchParams(body)) {
// body is a URLSearchParams
body = Buffer.from(body.toString());
} else if (body instanceof Blob) {
} else if (isBlob(body)) {
// body is blob
body = body[BUFFER];
} else if (Buffer.isBuffer(body)) {
// body is Buffer
} else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
@ -191,18 +190,25 @@ function consumeBody() {
return Body.Promise.reject(this[INTERNALS].error);
}
let body = this.body;
// body is null
if (this.body === null) {
if (body === null) {
return Body.Promise.resolve(Buffer.alloc(0));
}
// body is blob
if (isBlob(body)) {
body = body.stream();
}
// body is buffer
if (Buffer.isBuffer(this.body)) {
return Body.Promise.resolve(this.body);
if (Buffer.isBuffer(body)) {
return Body.Promise.resolve(body);
}
// istanbul ignore if: should never happen
if (!(this.body instanceof Stream)) {
if (!(body instanceof Stream)) {
return Body.Promise.resolve(Buffer.alloc(0));
}
@ -224,7 +230,7 @@ function consumeBody() {
}
// handle stream errors
this.body.on('error', err => {
body.on('error', err => {
if (err.name === 'AbortError') {
// if the request was aborted, reject with this Error
abort = true;
@ -235,7 +241,7 @@ function consumeBody() {
}
});
this.body.on('data', chunk => {
body.on('data', chunk => {
if (abort || chunk === null) {
return;
}
@ -250,7 +256,7 @@ function consumeBody() {
accum.push(chunk);
});
this.body.on('end', () => {
body.on('end', () => {
if (abort) {
return;
}
@ -355,6 +361,22 @@ function isURLSearchParams(obj) {
typeof obj.sort === 'function';
}
/**
* Check if `obj` is a W3C `Blob` object (which `File` inherits from)
* @param {*} obj
* @return {boolean}
*/
function isBlob(obj) {
return typeof obj === 'object' &&
typeof obj.arrayBuffer === 'function' &&
typeof obj.type === 'string' &&
typeof obj.stream === 'function' &&
typeof obj.constructor === 'function' &&
typeof obj.constructor.name === 'string' &&
/^(Blob|File)$/.test(obj.constructor.name) &&
/^(Blob|File)$/.test(obj[Symbol.toStringTag])
}
/**
* Clone body given Res/Req instance
*
@ -407,7 +429,7 @@ export function extractContentType(body) {
} else if (isURLSearchParams(body)) {
// body is a URLSearchParams
return 'application/x-www-form-urlencoded;charset=UTF-8';
} else if (body instanceof Blob) {
} else if (isBlob(body)) {
// body is blob
return body.type || null;
} else if (Buffer.isBuffer(body)) {
@ -448,6 +470,8 @@ export function getTotalBytes(instance) {
if (body === null) {
// body is null
return 0;
} else if (isBlob(body)) {
return body.size;
} else if (Buffer.isBuffer(body)) {
// body is buffer
return body.length;
@ -460,8 +484,7 @@ export function getTotalBytes(instance) {
return null;
} else {
// body is stream
// can't really do much about this
return null;
return instance.size || null;
}
}
@ -477,6 +500,8 @@ export function writeToStream(dest, instance) {
if (body === null) {
// body is null
dest.end();
} else if (isBlob(body)) {
body.stream().pipe(dest);
} else if (Buffer.isBuffer(body)) {
// body is buffer
dest.write(body);