azure
Version:
Microsoft Azure Client Library for node
208 lines (188 loc) • 6.59 kB
JavaScript
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
;
const request = require('request');
const through = require('through');
const duplexer = require('duplexer');
const Constants = require('./constants');
const utils = require('./utils');
const HttpVerbs = Constants.HttpConstants.HttpVerbs;
//
// Request pipelines are functions that allow you to
// add filters to modify requests and responses
// before / after the actual HTTP request.
//
/**
*
* create a new http client pipeline that ends with a call to the
* request library using the given sink function to actually make
* the http request.
*
* @return function(request, callback) - function to make a request.
*
*/
exports.createWithSink = function(sink) {
let pipeline = sink;
// The function that actually runs the pipeline. It starts simple
function runFilteredRequest(options, callback) {
return pipeline(options, callback);
}
function makeFilteredPipeline(filter) {
let currentPipeline = pipeline;
return function (options, callback) {
return filter(options, currentPipeline, callback);
};
}
// Add 'add' method so we can add filters.
runFilteredRequest.add = function add() {
let argumentList = utils.objectValues(arguments);
argumentList.forEach((filter) => {
pipeline = makeFilteredPipeline(filter);
});
};
// Add verb specific helper methods
let verbs = Object.keys(HttpVerbs);
verbs.forEach((method) => {
runFilteredRequest[method] = ((m) => {
return (options, callback) => {
options.method = m;
return pipeline(options, callback);
};
})(method);
});
// If user passed any other parameters, assume they're filters
// and add them.
for(let i = 1; i < arguments.length; ++i) {
runFilteredRequest.add(arguments[i]);
}
return runFilteredRequest;
};
/**
* This function acts as the final sink for a request, actually
* going out over the wire.
*
* @param {Object} [requestOptions] - The request options
* {@link https://github.com/request/request#requestoptions-callback Options doc}
*
* @private
* @param options The request to perform
* @param callback function(err, result, response, body) callback function that
* will be called at completion of the request.
*/
exports.requestLibrarySink = function requestLibrarySink (requestOptions) {
return (options, callback) => {
let defaultRequest = request.defaults(requestOptions);
let requestStream, bodyStream, requestHeaderStream;
if (options.headersOnly) {
requestHeaderStream = defaultRequest(options);
requestHeaderStream.on('error', (err) => {
return callback(err);
});
requestHeaderStream.on('response', (response) => {
requestHeaderStream.on('end', () => {
return callback(null, response);
});
});
return requestHeaderStream;
} else if (options.streamedResponse) {
if (options.body && typeof options.body.pipe === 'function') {
bodyStream = options.body;
options.body = null;
requestStream = bodyStream.pipe(defaultRequest(options));
} else {
requestStream = defaultRequest(options);
}
requestStream.on('error', (err) => {
return callback(err);
});
requestStream.on('response', (response) => {
return callback(null, response);
});
return requestStream;
} else if (options.body && typeof options.body.pipe === 'function') {
bodyStream = options.body;
options.body = null;
return bodyStream.pipe(defaultRequest(options, (err, response, body) => {
if (err) { return callback(err); }
return callback(null, response, body);
}));
} else {
return defaultRequest(options, (err, response, body) => {
if (err) { return callback(err); }
return callback(null, response, body);
});
}
};
};
/**
*
* create a new http client pipeline that ends with a call to the
* request library.
*
* @param {Object} [requestOptions] - The request options
* {@link https://github.com/request/request#requestoptions-callback Options doc}
*
* @return function(request, callback) - function to make a request.
*
*/
exports.create = function (requestOptions) {
return function () {
if (arguments.length === 0) {
return exports.createWithSink(exports.requestLibrarySink(requestOptions));
}
// User passed filters to add to the pipeline.
// build up appropriate arguments and call exports.createWithSink
return exports.createWithSink.apply(null, [exports.requestLibrarySink(requestOptions)].concat(utils.objectValues(arguments)));
};
};
/**
* Create a new filter that's a combination of all the filters
* given on the arguments list.
*
* @param {varargs} filters to run. First filter in the list is closest to
* the sink, so last to run before the request, first to run on the response:
* exactly the same as if you called pipeline.add or passed the list to
* pipeline.create.
*
* @return the new filter.
*/
exports.createCompositeFilter = function createCompositeFilter() {
let filter = arguments[0];
function makePairedFilter(filterA, filterB) {
return (options, next, callback) => {
function callFilterA(o, cb) {
return filterA(o, next, cb);
}
return filterB(options, callFilterA, callback);
};
}
for(let i = 1; i < arguments.length; ++i) {
filter = makePairedFilter(filter, arguments[i]);
}
return filter;
};
/**
* Creates an interim stream which can be returned to the
* caller synchronously, so that async operations can still
* hook up to the streaming output afterwards. Used when
* filters need to do async work before they can call the rest
* of the pipeline.
*
* @param setPipe function(input, output)
* this function is invoked synchronously, to pass the two
* underlying streams. input should be piped to the result of
* the next filter. The result of next should be piped to output.
* It's common to pause the input stream to prevent data loss
* before actually returning the real stream to hook up to.
*
* @returns a duplex stream that writes to the input stream and
* produces data from the output stream.
*/
exports.interimStream = function(setPipes) {
let input = through();
let output = through();
let duplex = duplexer(input, output);
setPipes(input, output);
return duplex;
};
exports = module.exports;