123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- "use strict";
- var _ = require('../../lodash'), PropertyBase = require('./property-base').PropertyBase, Property = require('./property').Property, Url = require('./url').Url,
- // ProxyConfig = require('./proxy-config').ProxyConfig,
- // Certificate = require('./certificate').Certificate,
- HeaderList = require('./header-list').HeaderList, RequestBody = require('./request-body').RequestBody,
- // RequestAuth = require('./request-auth').RequestAuth,
- Request,
- /**
- * Default request method
- *
- * @private
- * @const
- * @type {String}
- */
- DEFAULT_REQ_METHOD = 'GET',
- /**
- * Content length header name
- *
- * @private
- * @const
- * @type {String}
- */
- CONTENT_LENGTH = 'Content-Length',
- /**
- * Single space
- *
- * @private
- * @const
- * @type {String}
- */
- SP = ' ',
- /**
- * Carriage return + line feed
- *
- * @private
- * @const
- * @type {String}
- */
- CRLF = '\r\n',
- /**
- * HTTP version
- *
- * @private
- * @const
- * @type {String}
- */
- HTTP_X_X = 'HTTP/X.X',
- /**
- * @private
- * @type {Boolean}
- */
- supportsBuffer = (typeof Buffer !== undefined) && _.isFunction(Buffer.byteLength),
- /**
- * Source of request body size calculation.
- * Either computed from body or used Content-Length header value.
- *
- * @private
- * @const
- * @type {Object}
- */
- SIZE_SOURCE = {
- computed: 'COMPUTED',
- contentLength: 'CONTENT-LENGTH'
- };
- /**
- * @typedef Request.definition
- * @property {String|Url} url The URL of the request. This can be a {@link Url.definition} or a string.
- * @property {String} method The request method, e.g: "GET" or "POST".
- * @property {Array<Header.definition>} header The headers that should be sent as a part of this request.
- * @property {RequestBody.definition} body The request body definition.
- * @property {RequestAuth.definition} auth The authentication/signing information for this request.
- * @property {ProxyConfig.definition} proxy The proxy information for this request.
- * @property {Certificate.definition} certificate The certificate information for this request.
- */
- _.inherit((
- /**
- * A Postman HTTP request object.
- *
- * @constructor
- * @extends {Property}
- * @param {Request.definition} options
- */
- Request = function PostmanRequest(options) {
- // this constructor is intended to inherit and as such the super constructor is required to be executed
- Request.super_.apply(this, arguments);
- // if the definition is a string, it implies that this is a get of URL
- (typeof options === 'string') && (options = {
- url: options
- });
- // Create the default properties
- _.assign(this, /** @lends Request.prototype */ {
- /**
- * @type {Url}
- */
- url: new Url(),
- /**
- * @type {HeaderList}
- */
- headers: new HeaderList(this, options && options.header),
- // Although a similar check is being done in the .update call below, this handles falsy options as well.
- /**
- * @type {String}
- * @todo: Clean this up
- */
- // the negated condition is required to keep DEFAULT_REQ_METHOD as a fallback
- method: _.has(options, 'method') && !_.isNil(options.method) ?
- String(options.method).toUpperCase() : DEFAULT_REQ_METHOD
- });
- this.update(options);
- }), Property);
- _.assign(Request.prototype, /** @lends Request.prototype */ {
- /**
- * Updates the different properties of the request.
- *
- * @param {Request.definition} options
- */
- update: function (options) {
- // Nothing to do
- if (!options) {
- return;
- }
- // The existing url is updated.
- _.has(options, 'url') && this.url.update(options.url);
- // The existing list of headers must be cleared before adding the given headers to it.
- options.header && this.headers.repopulate(options.header);
- // Only update the method if one is provided.
- _.has(options, 'method') && (this.method = _.isNil(options.method) ?
- DEFAULT_REQ_METHOD : String(options.method).toUpperCase());
- // The rest of the properties are not assumed to exist so we merge in the defined ones.
- _.mergeDefined(this, /** @lends Request.prototype */ {
- /**
- * @type {RequestBody|undefined}
- */
- body: _.createDefined(options, 'body', RequestBody),
- });
- },
- /**
- * Returns an object where the key is a header name and value is the header value.
- *
- * @param {Object=} options
- * @param {Boolean} options.ignoreCase When set to "true", will ensure that all the header keys are lower case.
- * @param {Boolean} options.enabled Only get the enabled headers
- * @param {Boolean} options.multiValue When set to "true", duplicate header values will be stored in an array
- * @param {Boolean} options.sanitizeKeys When set to "true", headers with falsy keys are removed
- * @returns {Object}
- * @note If multiple headers are present in the same collection with same name, but different case
- * (E.g "x-forward-port" and "X-Forward-Port", and `options.ignoreCase` is set to true,
- * the values will be stored in an array.
- */
- getHeaders: function getHeaders(options) {
- !options && (options = {});
- // @note: options.multiValue will not be respected since, Header._postman_propertyAllowsMultipleValues
- // gets higher precedence in PropertyLists.toObject.
- // @todo: sanitizeKeys for headers by default.
- return this.headers.toObject(options.enabled, !options.ignoreCase, options.multiValue, options.sanitizeKeys);
- },
- /**
- * Calls the given callback on each Header object contained within the request.
- *
- * @param {Function} callback
- */
- forEachHeader: function forEachHeader(callback) {
- this.headers.all().forEach(function (header) {
- return callback(header, this);
- }, this);
- },
- /**
- * Adds a header to the PropertyList of headers.
- *
- * @param {Header| {key: String, value: String}} header Can be a {Header} object, or a raw header object.
- */
- addHeader: function (header) {
- this.headers.add(header);
- },
- /**
- * Removes a header from the request.
- *
- * @param {String|Header} toRemove A header object to remove, or a string containing the header key.
- * @param {Object} options
- * @param {Boolean} options.ignoreCase If set to true, ignores case while removing the header.
- */
- removeHeader: function (toRemove, options) {
- toRemove = _.isString(toRemove) ? toRemove : toRemove.key;
- options = options || {};
- if (!toRemove) { // Nothing to remove :(
- return;
- }
- options.ignoreCase && (toRemove = toRemove.toLowerCase());
- this.headers.remove(function (header) {
- var key = options.ignoreCase ? header.key.toLowerCase() : header.key;
- return key === toRemove;
- });
- },
- /**
- * Updates or inserts the given header.
- *
- * @param {Object} header
- */
- upsertHeader: function (header) {
- if (!(header && header.key)) {
- return;
- } // if no valid header is provided, do nothing
- var existing = this.headers.find({ key: header.key });
- if (!existing) {
- return this.headers.add(header);
- }
- existing.value = header.value;
- },
- /**
- * Add query parameters to the request.
- *
- * @todo: Rename this?
- * @param {Array<QueryParam>|String} params
- */
- addQueryParams: function (params) {
- this.url.addQueryParams(params);
- },
- /**
- * Removes parameters passed in params.
- *
- * @param {String|Array} params
- */
- removeQueryParams: function (params) {
- this.url.removeQueryParams(params);
- },
- /**
- * Get the request size by computing the headers and body or using the
- * actual content length header once the request is sent.
- *
- * @returns {Object}
- */
- size: function () {
- var contentLength = this.headers.get(CONTENT_LENGTH), requestTarget = this.url.getPathWithQuery(), bodyString, sizeInfo = {
- body: 0,
- header: 0,
- total: 0,
- source: SIZE_SOURCE.computed
- };
- // if 'Content-Length' header is present, we take body as declared by
- // the client(postman-request or user-defined). else we need to compute the same.
- if (contentLength && _.isNumeric(contentLength)) {
- sizeInfo.body = parseInt(contentLength, 10);
- sizeInfo.source = SIZE_SOURCE.contentLength;
- }
- // otherwise, if body is defined, we calculate the length of the body
- else if (this.body) {
- // @note body.toString() returns E for formdata or file mode
- bodyString = this.body.toString();
- sizeInfo.body = supportsBuffer ? Buffer.byteLength(bodyString) : bodyString.length;
- }
- // https://tools.ietf.org/html/rfc7230#section-3
- // HTTP-message = start-line (request-line / status-line)
- // *( header-field CRLF )
- // CRLF
- // [ message-body ]
- // request-line = method SP request-target SP HTTP-version CRLF
- sizeInfo.header = (this.method + SP + requestTarget + SP + HTTP_X_X + CRLF + CRLF).length +
- this.headers.contentSize();
- // compute the approximate total body size by adding size of header and body
- sizeInfo.total = (sizeInfo.body || 0) + (sizeInfo.header || 0);
- return sizeInfo;
- },
- /**
- * Converts the Request to a plain JavaScript object, which is also how the request is
- * represented in a collection file.
- *
- * @returns {{url: (*|string), method: *, header: (undefined|*), body: *, auth: *, certificate: *}}
- */
- toJSON: function () {
- var obj = PropertyBase.toJSON(this);
- // remove header array if blank
- if (_.isArray(obj.header) && !obj.header.length) {
- delete obj.header;
- }
- return obj;
- },
- /**
- * Creates a clone of this request
- *
- * @returns {Request}
- */
- clone: function () {
- return new Request(this.toJSON());
- },
- /**
- * Creates a copy of this request, with the appropriate auth headers or parameters added.
- *
- * @deprecated discontinued in v3.x
- * @note This function does not take care of resolving variables.
- * @returns {Request}
- */
- authorize: function () {
- throw new Error('collection request.authorize() has been discontinued');
- }
- });
- _.assign(Request, /** @lends Request */ {
- /**
- * Defines the name of this property for internal use.
- * @private
- * @readOnly
- * @type {String}
- */
- _postman_propertyName: 'Request',
- /**
- * Check whether an object is an instance of {@link ItemGroup}.
- *
- * @param {*} obj
- * @returns {Boolean}
- */
- isRequest: function (obj) {
- return Boolean(obj) && ((obj instanceof Request) ||
- _.inSuperChain(obj.constructor, '_postman_propertyName', Request._postman_propertyName));
- }
- });
- module.exports = {
- Request: Request
- };
|