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.

261 lines
9.4 KiB
JavaScript

"use strict"; // Standard library
9 years ago
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9 years ago
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
9 years ago
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
9 years ago
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
9 years ago
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
9 years ago
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
9 years ago
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
9 years ago
var path = require("path");
9 years ago
var stream = require("stream"); // Third-party dependencies
9 years ago
var uuid = require("uuid");
var mime = require("mime");
var combinedStream2 = require("combined-stream2");
var Promise = require("bluebird");
var debug = require("debug")("form-data2");
var CRLF = "\r\n"; // Utility functions
var ofTypes = function ofTypes(obj, types) {
var match = false;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = types[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var type = _step.value;
match = match || obj instanceof type;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
9 years ago
}
9 years ago
return match;
};
function assign() {
for (var _len = arguments.length, objects = new Array(_len), _key = 0; _key < _len; _key++) {
objects[_key] = arguments[_key];
}
var validObjects = objects.filter(function (object) {
return object != null;
});
if (validObjects.length === 0) {
return {};
} else if (validObjects.length === 1) {
return validObjects[0];
} else {
return Object.assign.apply(Object, _toConsumableArray(validObjects));
}
}
module.exports =
/*#__PURE__*/
function () {
9 years ago
function FormData() {
_classCallCheck(this, FormData);
9 years ago
this._firstHeader = false;
this._closingHeaderAppended = false;
this._boundary = "----" + uuid.v4();
this._headers = {
"content-type": "multipart/form-data; boundary=".concat(this._boundary)
9 years ago
};
this._stream = combinedStream2.create();
}
_createClass(FormData, [{
key: "_getStreamMetadata",
value: function _getStreamMetadata(source, options) {
// FIXME: Make work with deferred sources (ie. callback-provided)
var contentLength, contentType, filename, left;
debug("obtaining metadata for source: %s", source.toString().replace(/\n/g, "\\n").replace(/\r/g, "\\r"));
var fullPath = (left = options.filename != null ? options.filename : __guard__(source.client != null ? source.client._httpMessage : undefined, function (x) {
return x.path;
})) != null ? left : source.path;
if (fullPath != null) {
// This is a file...
var left1, left2;
filename = path.basename(fullPath);
contentType = (left1 = options.contentType != null ? options.contentType : source.headers != null ? source.headers["content-type"] : undefined) != null ? left1 : mime.lookup(filename);
contentLength = (left2 = options.knownLength != null ? options.knownLength : options.contentLength) != null ? left2 : source.headers != null ? source.headers["content-length"] : undefined; // FIXME: Is this even used anywhere?
} else {
// Probably just a plaintext form value, or an unidentified stream
var left3;
contentType = options.contentType != null ? options.contentType : source.headers != null ? source.headers["content-type"] : undefined;
contentLength = (left3 = options.knownLength != null ? options.knownLength : options.contentLength) != null ? left3 : source.headers != null ? source.headers["content-length"] : undefined;
}
return {
filename: filename,
contentType: contentType,
contentLength: contentLength
};
9 years ago
}
}, {
key: "_generateHeaderFields",
value: function _generateHeaderFields(name, metadata) {
debug("generating headers for: %s", metadata);
var headerFields = [];
if (metadata.filename != null) {
var escapedFilename = metadata.filename.replace('"', '\\"');
headerFields.push("Content-Disposition: form-data; name=\"".concat(name, "\"; filename=\"").concat(escapedFilename, "\""));
} else {
headerFields.push("Content-Disposition: form-data; name=\"".concat(name, "\""));
}
if (metadata.contentType != null) {
headerFields.push("Content-Type: ".concat(metadata.contentType));
}
debug("generated headers: %s", headerFields);
return headerFields.join(CRLF);
9 years ago
}
}, {
key: "_appendHeader",
value: function _appendHeader(name, metadata) {
var leadingCRLF;
if (this._firstHeader === false) {
debug("appending header");
leadingCRLF = "";
this._firstHeader = true;
} else {
debug("appending first header");
leadingCRLF = CRLF;
}
var headerFields = this._generateHeaderFields(name, metadata);
return this._stream.append(new Buffer(leadingCRLF + "--".concat(this._boundary) + CRLF + headerFields + CRLF + CRLF));
9 years ago
}
}, {
key: "_appendClosingHeader",
value: function _appendClosingHeader() {
debug("appending closing header");
return this._stream.append(new Buffer(CRLF + "--".concat(this._boundary, "--")));
9 years ago
}
}, {
key: "append",
value: function append(name, source, options) {
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 the string isn't UTF-8, this won't end well!
if (options.contentType == null) {
options.contentType = "text/plain";
}
}
var metadata = this._getStreamMetadata(source, options);
this._appendHeader(name, metadata);
return this._stream.append(source, options);
9 years ago
}
}, {
key: "done",
value: function done() {
// This method should be called when the user is finished adding streams. It adds the termination header at the end of the combined stream. When piping, this method is automatically called!
debug("called 'done'");
if (!this._closingHeaderAppended) {
this._closingHeaderAppended = true;
return this._appendClosingHeader();
9 years ago
}
}
}, {
key: "getBoundary",
value: function getBoundary() {
return this._boundary;
9 years ago
}
}, {
key: "getHeaders",
value: function getHeaders(callback) {
var _this = this;
9 years ago
// Returns the headers needed to correctly transmit the generated multipart/form-data blob. We will first need to call @done() to make sure that the multipart footer is there - from this point on, no new sources can be appended anymore.
this.done();
return Promise.try(function () {
9 years ago
return _this._stream.getCombinedStreamLength();
}).then(function (length) {
debug("total combined stream length: %s", length);
return Promise.resolve({
"content-length": length
});
}).catch(function (_error) {
// We couldn't get the stream length, most likely there was a stream involved that `stream-length` does not support.
debug("WARN: could not get total combined stream length");
return Promise.resolve({
"transfer-encoding": "chunked"
});
}).then(function (sizeHeaders) {
return Promise.resolve(assign(sizeHeaders, _this._headers));
}).nodeify(callback);
}
}, {
key: "getLength",
value: function getLength(callback) {
return this._stream.getCombinedStreamLength(callback);
}
}, {
key: "pipe",
value: function pipe(target) {
this.done(); // Pass through to the underlying `combined-stream`.
9 years ago
debug("piping underlying combined-stream2 to target writable");
return this._stream.pipe(target);
}
}]);
9 years ago
return FormData;
}();
9 years ago
function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null ? transform(value) : undefined;
}