diff options
Diffstat (limited to 'deps/npm/node_modules/node-gyp/node_modules/request/node_modules/form-data/lib/form_data.js')
-rw-r--r-- | deps/npm/node_modules/node-gyp/node_modules/request/node_modules/form-data/lib/form_data.js | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/deps/npm/node_modules/node-gyp/node_modules/request/node_modules/form-data/lib/form_data.js b/deps/npm/node_modules/node-gyp/node_modules/request/node_modules/form-data/lib/form_data.js new file mode 100644 index 0000000000..426b06f3b0 --- /dev/null +++ b/deps/npm/node_modules/node-gyp/node_modules/request/node_modules/form-data/lib/form_data.js @@ -0,0 +1,237 @@ +var CombinedStream = require('combined-stream'); +var util = require('util'); +var path = require('path'); +var http = require('http'); +var https = require('https'); +var parseUrl = require('url').parse; +var fs = require('fs'); +var mime = require('mime'); +var async = require('async'); + +module.exports = FormData; +function FormData() { + this._overheadLength = 0; + this._valueLength = 0; + this._lengthRetrievers = []; + + CombinedStream.call(this); +} +util.inherits(FormData, CombinedStream); + +FormData.LINE_BREAK = '\r\n'; + +FormData.prototype.append = function(field, value) { + var append = CombinedStream.prototype.append.bind(this); + + // all that streamy business can't handle numbers + if (typeof value == 'number') value = ''+value; + + var header = this._multiPartHeader(field, value); + var footer = this._multiPartFooter(field, value); + + append(header); + append(value); + append(footer); + + this._trackLength(header, value) +}; + +FormData.prototype._trackLength = function(header, value) { + var valueLength = 0; + if (Buffer.isBuffer(value)) { + valueLength = value.length; + } else if (typeof value === 'string') { + valueLength = Buffer.byteLength(value); + } + + this._valueLength += valueLength; + this._overheadLength += + Buffer.byteLength(header) + + + FormData.LINE_BREAK.length; + + // empty or ethier doesn't have path or not an http response + if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) { + return; + } + + this._lengthRetrievers.push(function(next) { + + // check if it's local file + if (value.hasOwnProperty('fd')) { + fs.stat(value.path, function(err, stat) { + if (err) { + next(err); + return; + } + + next(null, stat.size); + }); + + // 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(); + + // something else + } else { + next('Unknown stream'); + } + }); +}; + +FormData.prototype._multiPartHeader = function(field, value) { + var boundary = this.getBoundary(); + var header = + '--' + boundary + FormData.LINE_BREAK + + 'Content-Disposition: form-data; name="' + field + '"'; + + // fs- and request- streams have path property + // TODO: Use request's response mime-type + if (value.path) { + header += + '; filename="' + path.basename(value.path) + '"' + FormData.LINE_BREAK + + 'Content-Type: ' + mime.lookup(value.path); + + // http response has not + } else if (value.readable && value.hasOwnProperty('httpVersion')) { + header += + '; filename="' + path.basename(value.client._httpMessage.path) + '"' + FormData.LINE_BREAK + + 'Content-Type: ' + value.headers['content-type']; + } + + header += FormData.LINE_BREAK + FormData.LINE_BREAK; + return header; +}; + +FormData.prototype._multiPartFooter = function(field, value) { + return function(next) { + var footer = FormData.LINE_BREAK; + + var lastPart = (this._streams.length === 0); + if (lastPart) { + footer += this._lastBoundary(); + } + + next(footer); + }.bind(this); +}; + +FormData.prototype._lastBoundary = function() { + return '--' + this.getBoundary() + '--'; +}; + +FormData.prototype.getHeaders = function(userHeaders) { + var formHeaders = { + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + }; + + for (var header in userHeaders) { + formHeaders[header.toLowerCase()] = userHeaders[header]; + } + + return formHeaders; +} + +FormData.prototype.getCustomHeaders = function(contentType) { + contentType = contentType ? contentType : 'multipart/form-data'; + + var formHeaders = { + 'content-type': contentType + '; boundary=' + this.getBoundary(), + 'content-length': this.getLengthSync() + }; + + return formHeaders; +} + +FormData.prototype.getBoundary = function() { + if (!this._boundary) { + this._generateBoundary(); + } + + return this._boundary; +}; + +FormData.prototype._generateBoundary = function() { + // This generates a 50 character boundary similar to those used by Firefox. + // They are optimized for boyer-moore parsing. + var boundary = '--------------------------'; + for (var i = 0; i < 24; i++) { + boundary += Math.floor(Math.random() * 10).toString(16); + } + + this._boundary = boundary; +}; + +FormData.prototype.getLengthSync = function() { + var knownLength = this._overheadLength + this._valueLength; + + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + return knownLength; +}; + +FormData.prototype.getLength = function(cb) { + var knownLength = this._overheadLength + this._valueLength; + + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + if (!this._lengthRetrievers.length) { + process.nextTick(cb.bind(this, null, knownLength)); + return; + } + + async.parallel(this._lengthRetrievers, function(err, values) { + if (err) { + cb(err); + return; + } + + values.forEach(function(length) { + knownLength += length; + }); + + cb(null, knownLength); + }); +}; + +FormData.prototype.submit = function(url, cb) { + this.getLength(function(err, length) { + var request + , parsedUrl = parseUrl(url) + , options = { + method: 'post', + port: parsedUrl.port || 80, + path: parsedUrl.pathname, + headers: this.getHeaders({'Content-Length': length}), + host: parsedUrl.hostname + }; + + if (parsedUrl.protocol == 'https:') { + // override default port + if (!parsedUrl.port) options.port = 443; + request = https.request(options); + } else { + request = http.request(options); + } + + this.pipe(request); + if (cb) { + request.on('error', cb); + request.on('response', cb.bind(this, null)); + } + + return request; + }.bind(this)); +}; |