vsphere-infra
Version:
163 lines (155 loc) • 6.15 kB
text/typescript
/*
* Copyright 2014-2021 Firestack, all rights reserved.
*/
import * as url from 'url';
import * as fs from 'fs';
import axios from 'axios';
import { IHeaders, } from 'soap';
import * as request from 'request';
import * as httpNtlm from 'httpntlm';
export class CustomHttpClientOptions {
sdkCache = true;
sdkCacheProfile = 'default';
constructor(init?: Partial<CustomHttpClientOptions>) {
if (init) { Object.assign(this, init); }
if (!this.sdkCacheProfile) { this.sdkCacheProfile = 'default'; }
}
}
export const sdkCaches = {
'core-types.xsd': 1,
'query-messagetypes.xsd': 1,
'query-types.xsd': 1,
'reflect-messagetypes.xsd': 1,
'reflect-types.xsd': 1,
'vim-messagetypes.xsd': 1,
'vim-types.xsd': 1,
'vim.wsdl': 1,
};
export class CustomHttpClient {
// tslint:disable-next-line: variable-name
_request = request;
config: CustomHttpClientOptions;
constructor(options?: Partial<CustomHttpClientOptions>) {
this.config = new CustomHttpClientOptions(options);
}
/**
* Build the HTTP request (method, uri, headers, ...)
* @param rurl The resource url
* @param data The payload
* @param exheaders Extra http headers
* @param exoptions Extra options
* @returns The http request object for the `request` module
*/
buildRequest(rurl: string, data: any, exheaders?: IHeaders, exoptions?: any): any {
if (exoptions === void 0) { exoptions = {}; }
// tslint:disable-next-line: deprecation
const curl = url.parse(rurl);
// const secure = curl.protocol === 'https:';
const host = curl.hostname;
const port = parseInt(curl.port, 10);
// const path = [curl.pathname || '/', curl.search || '', curl.hash || ''].join('');
const method = data ? 'POST' : 'GET';
const headers = {
'User-Agent': 'node-soap-custom-http-client',
Accept: 'text/html,application/xhtml+xml,application/xml,text/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'none',
'Accept-Charset': 'utf-8',
Connection: exoptions.forever ? 'keep-alive' : 'close',
Host: host + (isNaN(port) ? '' : ':' + port)
};
const mergeOptions = ['headers'];
// tslint:disable-next-line: variable-name
const {attachments: _attachments, ...newExoptions } = exoptions;
const attachments = _attachments || [];
if (typeof data === 'string' && attachments.length === 0 && !exoptions.forceMTOM) {
headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
exheaders = exheaders || {};
for (const attr of Object.keys(exheaders)) { headers[attr] = exheaders[attr]; }
const options: any = {
uri: curl,
method, headers,
followAllRedirects: true
};
options.body = data;
for (const attr of Object.keys(newExoptions)) {
if (mergeOptions.indexOf(attr) !== -1) {
for (const header of Object.keys(exoptions[attr])) {
options[attr][header] = exoptions[attr][header];
}
} else {
options[attr] = exoptions[attr];
}
}
return options;
}
/**
* Handle the http response
* @param reqObj The req object
* @param res The res object
* @param body The http body
* @param The parsed body
*/
handleResponse(reqObj: any, res: any, body: any): any { return body; }
// handleResponse(reqObj: req.Request, res: req.Response, body: any): any { return body; }
/** Overriden to axios requests */
request(rurl: string, data: any, callback: (error: any, res?: any, body?: any) => any,
exheaders?: IHeaders, exoptions?: any, caller?: any): void {
const options = this.buildRequest(rurl, data, exheaders, exoptions);
let reqObj;
if (exoptions !== undefined && exoptions.hasOwnProperty('ntlm')) {
// sadly when using ntlm nothing to return
// Not sure if this can be handled in a cleaner way rather than an if/else,
// will to tidy up if I get chance later, patches welcome - insanityinside
// TODO - should the following be uri?
options.url = rurl;
httpNtlm[options.method.toLowerCase()](options, (err, res) => {
if (err) {
return callback(err);
}
// if result is stream
if (typeof res.body !== 'string') {
res.body = res.body.toString();
}
res.body = this.handleResponse(reqObj, res, res.body);
callback(null, res, res.body);
});
} else {
const urlObj = options.url = options.uri;
options.data = options.body;
// Use cached SDK interface definitions if hit
if (this.config.sdkCache && urlObj.path.startsWith('/sdk/')) {
const target = urlObj.path.split('/sdk/')[1];
if (sdkCaches[target]) {
fs.readFile(`${__dirname}/sdk-cache/${this.config.sdkCacheProfile}/${target}`, (e, dataBuffer) => {
// tslint:disable-next-line: no-console
if (e) { console.log(e); return; }
const dat = dataBuffer.toString('utf8');
const code = 200;
const res = { status: code, statusCode: code, body: dat, data: dat };
callback(null, res, res.body);
});
return;
}
}
// ! override to axios
if (typeof options.url === 'object') {
options.url = options.url.href;
}
reqObj = axios(options).then(res => {
(res as any).body = res.data;
(res as any).statusCode = res.status;
// fs.writeFile(`isolate-${Date.now()}.xml`, options.url.path + '\n' + res.data, () => {});
callback(null, res, res.data);
}).catch(e => {
callback(e);
});
}
}
// requestStream(rurl: string, data: any, exheaders?: IHeaders, exoptions?: any, caller?: any): req.Request {
requestStream(rurl: string, data: any, exheaders?: IHeaders, exoptions?: any, caller?: any): any {
const options = this.buildRequest(rurl, data, exheaders, exoptions);
return this._request(options);
}
}