You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
5.9 KiB
JavaScript

var CRLF, FormData, Promise, combinedStream2, debug, mime, ofTypes, path, stream, uuid, _;
path = require("path");
stream = require("stream");
uuid = require("uuid");
mime = require("mime");
combinedStream2 = require("combined-stream2");
Promise = require("bluebird");
_ = require("lodash");
debug = require("debug")("form-data2");
CRLF = "\r\n";
ofTypes = function(obj, types) {
var match, type, _i, _len;
match = false;
for (_i = 0, _len = types.length; _i < _len; _i++) {
type = types[_i];
match = match || obj instanceof type;
}
return match;
};
module.exports = FormData = (function() {
function FormData() {
this._firstHeader = false;
this._closingHeaderAppended = false;
this._boundary = "----" + uuid.v4();
this._headers = {
"content-type": "multipart/form-data; boundary=" + this._boundary
};
this._stream = combinedStream2.create();
}
FormData.prototype._getStreamMetadata = function(source, options) {
var contentLength, contentType, filename, fullPath, _ref, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
debug("obtaining metadata for source: %s", source.toString().replace(/\n/g, "\\n").replace(/\r/g, "\\r"));
fullPath = (_ref = (_ref1 = options.filename) != null ? _ref1 : (_ref2 = source.client) != null ? (_ref3 = _ref2._httpMessage) != null ? _ref3.path : void 0 : void 0) != null ? _ref : source.path;
if (fullPath != null) {
filename = path.basename(fullPath);
contentType = (_ref4 = (_ref5 = options.contentType) != null ? _ref5 : (_ref6 = source.headers) != null ? _ref6["content-type"] : void 0) != null ? _ref4 : mime.lookup(filename);
contentLength = (_ref7 = (_ref8 = options.knownLength) != null ? _ref8 : options.contentLength) != null ? _ref7 : (_ref9 = source.headers) != null ? _ref9["content-length"] : void 0;
} else {
contentType = (_ref10 = options.contentType) != null ? _ref10 : (_ref11 = source.headers) != null ? _ref11["content-type"] : void 0;
contentLength = (_ref12 = (_ref13 = options.knownLength) != null ? _ref13 : options.contentLength) != null ? _ref12 : (_ref14 = source.headers) != null ? _ref14["content-length"] : void 0;
}
return {
filename: filename,
contentType: contentType,
contentLength: contentLength
};
};
FormData.prototype._generateHeaderFields = function(name, metadata) {
var escapedFilename, headerFields;
debug("generating headers for: %s", metadata);
headerFields = [];
if (metadata.filename != null) {
escapedFilename = metadata.filename.replace('"', '\\"');
headerFields.push("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + escapedFilename + "\"");
} else {
headerFields.push("Content-Disposition: form-data; name=\"" + name + "\"");
}
if (metadata.contentType != null) {
headerFields.push("Content-Type: " + metadata.contentType);
}
debug("generated headers: %s", headerFields);
return headerFields.join(CRLF);
};
FormData.prototype._appendHeader = function(name, metadata) {
var headerFields, leadingCRLF;
if (this._firstHeader === false) {
debug("appending header");
leadingCRLF = "";
this._firstHeader = true;
} else {
debug("appending first header");
leadingCRLF = CRLF;
}
headerFields = this._generateHeaderFields(name, metadata);
return this._stream.append(new Buffer(leadingCRLF + ("--" + this._boundary) + CRLF + headerFields + CRLF + CRLF));
};
FormData.prototype._appendClosingHeader = function() {
debug("appending closing header");
return this._stream.append(new Buffer(CRLF + ("--" + this._boundary + "--")));
};
FormData.prototype.append = function(name, source, options) {
var metadata;
if (options == null) {
options = {};
}
debug("appending source");
if (this._closingHeaderAppended) {
throw new Error("The stream has already been prepared for usage; you either piped it or generated the HTTP headers. No new sources can be appended anymore.");
}
if (!ofTypes(source, [stream.Readable, stream.Duplex, stream.Transform, Buffer, Function]) && typeof source !== "string") {
throw new Error("The provided value must be either a readable stream, a Buffer, a callback providing either of those, or a string.");
}
if (typeof source === "string") {
source = new Buffer(source);
if (options.contentType == null) {
options.contentType = "text/plain";
}
}
metadata = this._getStreamMetadata(source, options);
this._appendHeader(name, metadata);
return this._stream.append(source, options);
};
FormData.prototype.done = function() {
debug("called 'done'");
if (!this._closingHeaderAppended) {
this._closingHeaderAppended = true;
return this._appendClosingHeader();
}
};
FormData.prototype.getBoundary = function() {
return this._boundary;
};
FormData.prototype.getHeaders = function(callback) {
this.done();
return Promise["try"]((function(_this) {
return function() {
return _this._stream.getCombinedStreamLength();
};
})(this)).then(function(length) {
debug("total combined stream length: %s", length);
return Promise.resolve({
"content-length": length
});
})["catch"](function(err) {
debug("WARN: could not get total combined stream length");
return Promise.resolve({
"transfer-encoding": "chunked"
});
}).then((function(_this) {
return function(sizeHeaders) {
return Promise.resolve(_.extend(sizeHeaders, _this._headers));
};
})(this)).nodeify(callback);
};
FormData.prototype.getLength = function(callback) {
return this._stream.getCombinedStreamLength(callback);
};
FormData.prototype.pipe = function(target) {
this.done();
debug("piping underlying combined-stream2 to target writable");
return this._stream.pipe(target);
};
return FormData;
})();