Comparing version 1.0.0-rc3 to 1.0.0-rc4
@@ -1,1 +0,2 @@ | ||
module.exports = FormData; | ||
/* eslint-env browser */ | ||
module.exports = FormData; |
@@ -10,5 +10,22 @@ var CombinedStream = require('combined-stream'); | ||
var async = require('async'); | ||
var populate = require('./populate.js'); | ||
// Public API | ||
module.exports = FormData; | ||
// make it a Stream | ||
util.inherits(FormData, CombinedStream); | ||
/** | ||
* Create readable "multipart/form-data" streams. | ||
* Can be used to submit forms | ||
* and file uploads to other web applications. | ||
* | ||
* @constructor | ||
*/ | ||
function FormData() { | ||
if (!(this instanceof FormData)) { | ||
throw new TypeError('Failed to construct FormData: Please use the _new_ operator, this object constructor cannot be called as a function.'); | ||
} | ||
this._overheadLength = 0; | ||
@@ -20,3 +37,2 @@ this._valueLength = 0; | ||
} | ||
util.inherits(FormData, CombinedStream); | ||
@@ -27,10 +43,16 @@ FormData.LINE_BREAK = '\r\n'; | ||
FormData.prototype.append = function(field, value, options) { | ||
options = (typeof options === 'string') | ||
? { filename: options } | ||
: options || {}; | ||
options = options || {}; | ||
// allow filename as single option | ||
if (typeof options == 'string') { | ||
options = {filename: options}; | ||
} | ||
var append = CombinedStream.prototype.append.bind(this); | ||
// all that streamy business can't handle numbers | ||
if (typeof value == 'number') value = ''+value; | ||
if (typeof value == 'number') { | ||
value = '' + value; | ||
} | ||
@@ -46,3 +68,3 @@ // https://github.com/felixge/node-form-data/issues/38 | ||
var header = this._multiPartHeader(field, value, options); | ||
var footer = this._multiPartFooter(field, value, options); | ||
var footer = this._multiPartFooter(); | ||
@@ -85,57 +107,58 @@ append(header); | ||
// no need to bother with the length | ||
if (!options.knownLength) | ||
this._lengthRetrievers.push(function(next) { | ||
if (!options.knownLength) { | ||
this._lengthRetrievers.push(function(next) { | ||
if (value.hasOwnProperty('fd')) { | ||
if (value.hasOwnProperty('fd')) { | ||
// take read range into a account | ||
// `end` = Infinity –> read file till the end | ||
// | ||
// TODO: Looks like there is bug in Node fs.createReadStream | ||
// it doesn't respect `end` options without `start` options | ||
// Fix it when node fixes it. | ||
// https://github.com/joyent/node/issues/7819 | ||
if (value.end != undefined && value.end != Infinity && value.start != undefined) { | ||
// take read range into a account | ||
// `end` = Infinity –> read file till the end | ||
// | ||
// TODO: Looks like there is bug in Node fs.createReadStream | ||
// it doesn't respect `end` options without `start` options | ||
// Fix it when node fixes it. | ||
// https://github.com/joyent/node/issues/7819 | ||
if (value.end != undefined && value.end != Infinity && value.start != undefined) { | ||
// when end specified | ||
// no need to calculate range | ||
// inclusive, starts with 0 | ||
next(null, value.end+1 - (value.start ? value.start : 0)); | ||
// when end specified | ||
// no need to calculate range | ||
// inclusive, starts with 0 | ||
next(null, value.end + 1 - (value.start ? value.start : 0)); | ||
// not that fast snoopy | ||
} else { | ||
// still need to fetch file size from fs | ||
fs.stat(value.path, function(err, stat) { | ||
// not that fast snoopy | ||
} else { | ||
// still need to fetch file size from fs | ||
fs.stat(value.path, function(err, stat) { | ||
var fileSize; | ||
var fileSize; | ||
if (err) { | ||
next(err); | ||
return; | ||
} | ||
if (err) { | ||
next(err); | ||
return; | ||
} | ||
// update final size based on the range options | ||
fileSize = stat.size - (value.start ? value.start : 0); | ||
next(null, fileSize); | ||
}); | ||
} | ||
// update final size based on the range options | ||
fileSize = stat.size - (value.start ? value.start : 0); | ||
next(null, fileSize); | ||
}); | ||
} | ||
// or http response | ||
} else if (value.hasOwnProperty('httpVersion')) { | ||
next(null, +value.headers['content-length']); | ||
// or http response | ||
} else if (value.hasOwnProperty('httpVersion')) { | ||
next(null, +value.headers['content-length']); | ||
// or request stream http://github.com/mikeal/request | ||
} else if (value.hasOwnProperty('httpModule')) { | ||
// wait till response come back | ||
value.on('response', function(response) { | ||
value.pause(); | ||
next(null, +response.headers['content-length']); | ||
}); | ||
value.resume(); | ||
// or request stream http://github.com/mikeal/request | ||
} else if (value.hasOwnProperty('httpModule')) { | ||
// wait till response come back | ||
value.on('response', function(response) { | ||
value.pause(); | ||
next(null, +response.headers['content-length']); | ||
}); | ||
value.resume(); | ||
// something else | ||
} else { | ||
next('Unknown stream'); | ||
} | ||
}); | ||
// something else | ||
} else { | ||
next('Unknown stream'); | ||
} | ||
}); | ||
} | ||
}; | ||
@@ -147,43 +170,17 @@ | ||
// (e.g. to handle extra CRLFs on .NET servers) | ||
if (options.header != null) { | ||
if (options.header) { | ||
return options.header; | ||
} | ||
var contentDisposition = this._getContentDisposition(value, options); | ||
var contentType = this._getContentType(value, options); | ||
var contents = ''; | ||
var headers = { | ||
'Content-Disposition': ['form-data', 'name="' + field + '"'], | ||
'Content-Type': [] | ||
// add custom disposition as third element or keep it two elements if not | ||
'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), | ||
// if no content type. allow it to be empty array | ||
'Content-Type': [].concat(contentType || []) | ||
}; | ||
// fs- and request- streams have path property | ||
// or use custom filename and/or contentType | ||
// TODO: Use request's response mime-type | ||
if (options.filename || value.path) { | ||
headers['Content-Disposition'].push( | ||
'filename="' + path.basename(options.filename || value.path) + '"' | ||
); | ||
headers['Content-Type'].push( | ||
options.contentType || | ||
mime.lookup(options.filename || value.path) || | ||
FormData.DEFAULT_CONTENT_TYPE | ||
); | ||
// http response has not | ||
} else if (value.readable && value.hasOwnProperty('httpVersion')) { | ||
headers['Content-Disposition'].push( | ||
'filename="' + path.basename(value.client._httpMessage.path) + '"' | ||
); | ||
headers['Content-Type'].push( | ||
options.contentType || | ||
value.headers['content-type'] || | ||
FormData.DEFAULT_CONTENT_TYPE | ||
); | ||
} else if (Buffer.isBuffer(value)) { | ||
headers['Content-Type'].push( | ||
options.contentType || | ||
FormData.DEFAULT_CONTENT_TYPE | ||
); | ||
} else if (options.contentType) { | ||
headers['Content-Type'].push(options.contentType); | ||
} | ||
for (var prop in headers) { | ||
@@ -194,7 +191,55 @@ if (headers[prop].length) { | ||
} | ||
return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; | ||
}; | ||
FormData.prototype._multiPartFooter = function(field, value, options) { | ||
FormData.prototype._getContentDisposition = function(value, options) { | ||
var contentDisposition; | ||
// custom filename takes precedence | ||
// fs- and request- streams have path property | ||
var filename = options.filename || value.path; | ||
// or try http response | ||
if (!filename && value.readable && value.hasOwnProperty('httpVersion')) { | ||
filename = value.client._httpMessage.path; | ||
} | ||
if (filename) { | ||
contentDisposition = 'filename="' + path.basename(filename) + '"'; | ||
} | ||
return contentDisposition; | ||
}; | ||
FormData.prototype._getContentType = function(value, options) { | ||
// use custom content-type above all | ||
var contentType = options.contentType; | ||
// or try `path` from fs-, request- streams | ||
if (!contentType && value.path) { | ||
contentType = mime.lookup(value.path); | ||
} | ||
// or if it's http-reponse | ||
if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { | ||
contentType = value.headers['content-type']; | ||
} | ||
// or guess it from the filename | ||
if (!contentType && options.filename) { | ||
contentType = mime.lookup(options.filename); | ||
} | ||
// fallback to the default content type if `value` is not simple value | ||
if (!contentType && typeof value == 'object') { | ||
contentType = FormData.DEFAULT_CONTENT_TYPE; | ||
} | ||
return contentType; | ||
}; | ||
FormData.prototype._multiPartFooter = function() { | ||
return function(next) { | ||
@@ -217,2 +262,3 @@ var footer = FormData.LINE_BREAK; | ||
FormData.prototype.getHeaders = function(userHeaders) { | ||
var header; | ||
var formHeaders = { | ||
@@ -222,19 +268,21 @@ 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() | ||
for (var header in userHeaders) { | ||
formHeaders[header.toLowerCase()] = userHeaders[header]; | ||
for (header in userHeaders) { | ||
if (userHeaders.hasOwnProperty(header)) { | ||
formHeaders[header.toLowerCase()] = userHeaders[header]; | ||
} | ||
} | ||
return formHeaders; | ||
} | ||
}; | ||
FormData.prototype.getCustomHeaders = function(contentType) { | ||
contentType = contentType ? contentType : 'multipart/form-data'; | ||
contentType = contentType ? contentType : 'multipart/form-data'; | ||
var formHeaders = { | ||
'content-type': contentType + '; boundary=' + this.getBoundary(), | ||
'content-length': this.getLengthSync() | ||
}; | ||
var formHeaders = { | ||
'content-type': contentType + '; boundary=' + this.getBoundary(), | ||
'content-length': this.getLengthSync() | ||
}; | ||
return formHeaders; | ||
} | ||
return formHeaders; | ||
}; | ||
@@ -263,3 +311,3 @@ FormData.prototype.getBoundary = function() { | ||
// and add it as knownLength option | ||
FormData.prototype.getLengthSync = function(debug) { | ||
FormData.prototype.getLengthSync = function() { | ||
var knownLength = this._overheadLength + this._valueLength; | ||
@@ -273,5 +321,5 @@ | ||
// https://github.com/felixge/node-form-data/issues/40 | ||
// https://github.com/form-data/form-data/issues/40 | ||
if (this._lengthRetrievers.length) { | ||
// Some async length retrivers are present | ||
// Some async length retrievers are present | ||
// therefore synchronous length calculation is false. | ||
@@ -312,8 +360,6 @@ // Please use getLength(callback) to get proper length | ||
FormData.prototype.submit = function(params, cb) { | ||
var request | ||
, options | ||
, defaults = { | ||
method : 'post' | ||
}; | ||
, defaults = {method: 'post'} | ||
; | ||
@@ -323,4 +369,4 @@ // parse provided url if it's string | ||
if (typeof params == 'string') { | ||
params = parseUrl(params); | ||
options = populate({ | ||
@@ -331,5 +377,6 @@ port: params.port, | ||
}, defaults); | ||
} | ||
else // use custom params | ||
{ | ||
// use custom params | ||
} else { | ||
options = populate(params, defaults); | ||
@@ -354,5 +401,7 @@ // if no port provided use default one | ||
this.getLength(function(err, length) { | ||
if (err) { | ||
this._error(err); | ||
return; | ||
} | ||
// TODO: Add chunked encoding when no length (if err) | ||
// add content length | ||
@@ -372,19 +421,7 @@ request.setHeader('Content-Length', length); | ||
FormData.prototype._error = function(err) { | ||
if (this.error) return; | ||
this.error = err; | ||
this.pause(); | ||
this.emit('error', err); | ||
if (!this.error) { | ||
this.error = err; | ||
this.pause(); | ||
this.emit('error', err); | ||
} | ||
}; | ||
/* | ||
* Santa's little helpers | ||
*/ | ||
// populates missing values | ||
function populate(dst, src) { | ||
for (var prop in src) { | ||
if (!dst[prop]) dst[prop] = src[prop]; | ||
} | ||
return dst; | ||
} |
@@ -5,3 +5,3 @@ { | ||
"description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", | ||
"version": "1.0.0-rc3", | ||
"version": "1.0.0-rc4", | ||
"repository": { | ||
@@ -14,6 +14,15 @@ "type": "git", | ||
"scripts": { | ||
"test": "./test/run.js" | ||
"pretest": "rimraf coverage test/tmp", | ||
"test": "istanbul cover --report none test/run.js", | ||
"posttest": "istanbul report", | ||
"lint": "eslint lib/*.js test/*.js test/**/*.js", | ||
"predebug": "rimraf coverage test/tmp", | ||
"debug": "verbose=1 ./test/run.js", | ||
"check": "istanbul check-coverage coverage/coverage*.json", | ||
"coverage": "codacy-coverage < ./coverage/lcov.info; true" | ||
}, | ||
"pre-commit": [ | ||
"test" | ||
"lint", | ||
"test", | ||
"check" | ||
], | ||
@@ -24,14 +33,20 @@ "engines": { | ||
"dependencies": { | ||
"async": "^1.4.0", | ||
"async": "^1.5.2", | ||
"combined-stream": "^1.0.5", | ||
"mime-types": "^2.1.3" | ||
"mime-types": "^2.1.10" | ||
}, | ||
"license": "MIT", | ||
"devDependencies": { | ||
"codacy-coverage": "^1.1.3", | ||
"coveralls": "^2.11.8", | ||
"cross-spawn": "^2.1.5", | ||
"eslint": "^2.4.0", | ||
"fake": "^0.2.2", | ||
"far": "^0.0.7", | ||
"formidable": "^1.0.17", | ||
"pre-commit": "^1.0.10", | ||
"request": "^2.60.0" | ||
"istanbul": "^0.4.2", | ||
"pre-commit": "^1.1.2", | ||
"request": "^2.69.0", | ||
"rimraf": "^2.5.2" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
23966
11
337
219
2
11
Updatedasync@^1.5.2
Updatedmime-types@^2.1.10