More exact content-type and content-length

Set content-type of requests with body being objects to text/plain
This commit is contained in:
Timothy Gu 2016-12-05 20:25:13 -08:00
parent 385ca6b2b0
commit a604069860
4 changed files with 97 additions and 31 deletions

View File

@ -27,8 +27,21 @@ export default class Body {
size = 0,
timeout = 0
} = {}) {
if (body instanceof Blob) {
body = body[BUFFER];
if (body == null) {
// body is undefined or null
body = null;
} else if (typeof body === 'string') {
// body is string
} else if (body instanceof Blob) {
// body is blob
} else if (Buffer.isBuffer(body)) {
// body is buffer
} else if (bodyStream(body)) {
// body is stream
} else {
// none of the above
// coerce to string
body = String(body);
}
this.body = body;
this[DISTURBED] = false;
@ -117,7 +130,7 @@ export default class Body {
this[DISTURBED] = true;
// body is null
if (!this.body) {
if (this.body === null) {
return Body.Promise.resolve(new Buffer(0));
}
@ -126,11 +139,21 @@ export default class Body {
return Body.Promise.resolve(new Buffer(this.body));
}
// body is blob
if (this.body instanceof Blob) {
return Body.Promise.resolve(this.body[BUFFER]);
}
// body is buffer
if (Buffer.isBuffer(this.body)) {
return Body.Promise.resolve(this.body);
}
// should never happen
if (!bodyStream(this.body)) {
return Body.Promise.resolve(new Buffer(0));
}
// body is stream
// get ready to actually consume the body
let accum = [];
@ -281,30 +304,80 @@ export function clone(instance) {
* @param Mixed instance Response or Request instance
*/
export function extractContentType(instance) {
// detect form data input from form-data module
if (typeof instance.body.getBoundary === 'function') {
return `multipart/form-data;boundary=${instance.body.getBoundary()}`;
}
const {body} = instance;
if (typeof instance.body === 'string') {
if (body === null) {
// body is null
return null;
} else if (typeof body === 'string') {
// body is string
return 'text/plain;charset=UTF-8';
} else if (body instanceof Blob) {
// body is blob
return body.type || null;
} else if (Buffer.isBuffer(body)) {
// body is buffer
return null;
} else if (typeof body.getBoundary === 'function') {
// detect form data input from form-data module
return `multipart/form-data;boundary=${body.getBoundary()}`;
} else {
// body is stream
// can't really do much about this
return null;
}
}
export function getTotalBytes(instance) {
const {body} = instance;
if (typeof body === 'string') {
if (body === null) {
// body is null
return 0;
} else if (typeof body === 'string') {
// body is string
return Buffer.byteLength(body);
} else if (body instanceof Blob) {
// body is blob
return body.size;
} else if (body && typeof body.getLengthSync === 'function') {
// detect form data input from form-data module
if (body._lengthRetrievers && body._lengthRetrievers.length == 0 || // 1.x
body.hasKnownLength && body.hasKnownLength()) { // 2.x
return body.getLengthSync();
}
} else if (body === undefined || body === null) {
// this is only necessary for older nodejs releases (before iojs merge)
return 0;
return null;
} else {
// body is stream
// can't really do much about this
return null;
}
}
export function writeToStream(dest, instance) {
const {body} = instance;
if (body === null) {
// body is null
dest.end();
} else if (typeof body === 'string') {
// body is string
dest.write(body);
dest.end();
} else if (body instanceof Blob) {
// body is blob
dest.write(body[BUFFER]);
dest.end();
} else if (Buffer.isBuffer(body)) {
// body is buffer
dest.write(body);
dest.end()
} else if (bodyStream(body)) {
// body is stream
body.pipe(dest);
} else {
// should never happen
dest.end();
}
}

View File

@ -11,7 +11,7 @@ import * as https from 'https';
import * as zlib from 'zlib';
import {PassThrough} from 'stream';
import Body from './body';
import Body, { writeToStream } from './body';
import Response from './response';
import Headers from './headers';
import Request, { getNodeRequestOptions } from './request';
@ -184,22 +184,7 @@ function fetch(url, opts) {
return;
});
// accept string, buffer, readable stream or null as body
// per spec we will call tostring on non-stream objects
if (typeof request.body === 'string') {
req.write(request.body);
req.end();
} else if (request.body instanceof Buffer) {
req.write(request.body);
req.end()
} else if (request.body && typeof request.body === 'object' && request.body.pipe) {
request.body.pipe(req);
} else if (request.body && typeof request.body === 'object') {
req.write(request.body.toString());
req.end();
} else {
req.end();
}
writeToStream(req, request);
});
};

View File

@ -63,7 +63,7 @@ export default class Request extends Body {
if (init.body != null) {
const contentType = extractContentType(this);
if (contentType && !this.headers.has('Content-Type')) {
if (contentType !== null && !this.headers.has('Content-Type')) {
this.headers.append('Content-Type', contentType);
}
}

View File

@ -814,6 +814,8 @@ describe(`node-fetch with FOLLOW_SPEC = ${defaultFollowSpec}`, () => {
}).then(res => {
expect(res.method).to.equal('POST');
expect(res.body).to.equal('[object Object]');
expect(res.headers['content-type']).to.equal('text/plain;charset=UTF-8');
expect(res.headers['content-length']).to.equal('15');
});
});
@ -1446,14 +1448,20 @@ describe(`node-fetch with FOLLOW_SPEC = ${defaultFollowSpec}`, () => {
it('should support blob round-trip', function() {
url = `${base}hello`;
let length, type;
return fetch(url).then(res => res.blob()).then(blob => {
url = `${base}inspect`;
length = blob.size;
type = blob.type;
return fetch(url, {
method: 'POST',
body: blob
});
}).then(res => res.json()).then(({body}) => {
}).then(res => res.json()).then(({body, headers}) => {
expect(body).to.equal('world');
expect(headers['content-type']).to.equal(type);
expect(headers['content-length']).to.equal(String(length));
});
});