1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _createClass = function () { 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
8 |
|
9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
10 |
|
11 | var https = require("https");
|
12 | var http = require("http");
|
13 | var request = require("request");
|
14 | var querystring = require("querystring");
|
15 |
|
16 | var HttpClient = function () {
|
17 | function HttpClient(options, credentials) {
|
18 | _classCallCheck(this, HttpClient);
|
19 |
|
20 | this.credentials = credentials;
|
21 | this.host = options.host || "rest.nexmo.com";
|
22 | this.port = options.port || 443;
|
23 | this.https = options.https || https;
|
24 | this.http = options.http || http;
|
25 | this.headers = {
|
26 | "Content-Type": "application/x-www-form-urlencoded",
|
27 | Accept: "application/json"
|
28 | };
|
29 | this.logger = options.logger;
|
30 | this.timeout = options.timeout;
|
31 | this.requestLib = request;
|
32 |
|
33 | if (options.userAgent) {
|
34 | this.headers["User-Agent"] = options.userAgent;
|
35 | }
|
36 | }
|
37 |
|
38 | _createClass(HttpClient, [{
|
39 | key: "request",
|
40 | value: function request(endpoint, method, callback) {
|
41 | var _this = this;
|
42 |
|
43 | var skipJsonParsing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
44 |
|
45 | if (typeof method === "function") {
|
46 | callback = method;
|
47 | endpoint.method = endpoint.method || "GET";
|
48 | } else if (typeof method !== "undefined") {
|
49 | endpoint.method = method;
|
50 | }
|
51 |
|
52 | if (endpoint.method === "POST" || endpoint.method === "DELETE") {
|
53 | // TODO: verify the following fix is required
|
54 | // Fix broken due ot 411 Content-Length error now sent by Nexmo API
|
55 | // PL 2016-Sept-6 - commented out Content-Length 0
|
56 | // headers['Content-Length'] = 0;
|
57 | }
|
58 | var options = {
|
59 | host: endpoint.host ? endpoint.host : this.host,
|
60 | port: this.port,
|
61 | path: endpoint.path,
|
62 | method: endpoint.method,
|
63 | headers: Object.assign({}, this.headers)
|
64 | };
|
65 |
|
66 | if (this.timeout !== undefined) {
|
67 | options.timeout = this.timeout;
|
68 | }
|
69 |
|
70 | // Allow existing headers to be overridden
|
71 | // Allow new headers to be added
|
72 | if (endpoint.headers) {
|
73 | Object.keys(endpoint.headers).forEach(function (key) {
|
74 | options.headers[key] = endpoint.headers[key];
|
75 | });
|
76 | }
|
77 |
|
78 | this.logger.info("Request:", options, "\nBody:", endpoint.body);
|
79 | var request;
|
80 |
|
81 | if (options.port === 443) {
|
82 | request = this.https.request(options);
|
83 | } else {
|
84 | request = this.http.request(options);
|
85 | }
|
86 |
|
87 | request.end(endpoint.body);
|
88 |
|
89 | // Keep an array of String or Buffers,
|
90 | // depending on content type (binary or JSON) of response
|
91 | var responseData = [];
|
92 |
|
93 | request.on("response", function (response) {
|
94 | var isBinary = response.headers["content-type"] === "application/octet-stream";
|
95 | if (!isBinary) {
|
96 | response.setEncoding("utf8");
|
97 | }
|
98 |
|
99 | response.on("data", function (chunk) {
|
100 | responseData.push(chunk);
|
101 | });
|
102 |
|
103 | response.on("end", function () {
|
104 | _this.logger.info("response ended:", response.statusCode);
|
105 | if (callback) {
|
106 | if (isBinary) {
|
107 | responseData = Buffer.concat(responseData);
|
108 | }
|
109 |
|
110 | _this.__parseResponse(response, responseData, endpoint.method, callback, skipJsonParsing);
|
111 | }
|
112 | });
|
113 | response.on("close", function (e) {
|
114 | if (e) {
|
115 | _this.logger.error("problem with API request detailed stacktrace below ");
|
116 | _this.logger.error(e);
|
117 | callback(e);
|
118 | }
|
119 | });
|
120 | });
|
121 | request.on("error", function (e) {
|
122 | _this.logger.error("problem with API request detailed stacktrace below ");
|
123 | _this.logger.error(e);
|
124 | callback(e);
|
125 | });
|
126 | }
|
127 | }, {
|
128 | key: "__parseResponse",
|
129 | value: function __parseResponse(httpResponse, data, method, callback, skipJsonParsing) {
|
130 | var isArrayOrBuffer = data instanceof Array || data instanceof Buffer;
|
131 | if (!isArrayOrBuffer) {
|
132 | throw new Error("data should be of type Array or Buffer");
|
133 | }
|
134 |
|
135 | var status = httpResponse.statusCode;
|
136 | var headers = httpResponse.headers;
|
137 |
|
138 | var response = null;
|
139 | var error = null;
|
140 |
|
141 | try {
|
142 | if (status >= 500) {
|
143 | error = { message: "Server Error", statusCode: status };
|
144 | } else if (httpResponse.headers["content-type"] === "application/octet-stream") {
|
145 | response = data;
|
146 | } else if (status === 429) {
|
147 | // 429 does not return a parsable body
|
148 | if (!headers["retry-after"]) {
|
149 | // retry based on allowed per second
|
150 | var retryAfterMillis = method === "POST" ? 1000 / 2 : 1000 / 5;
|
151 | headers["retry-after"] = retryAfterMillis;
|
152 | }
|
153 | error = { body: data.join("") };
|
154 | } else if (status === 204) {
|
155 | response = null;
|
156 | } else if (status >= 400 || status < 200) {
|
157 | error = { body: JSON.parse(data.join("")), headers: headers };
|
158 | } else if (method !== "DELETE") {
|
159 | if (!!skipJsonParsing) {
|
160 | response = data.join("");
|
161 | } else {
|
162 | response = JSON.parse(data.join(""));
|
163 | }
|
164 | } else {
|
165 | response = data;
|
166 | }
|
167 | } catch (parseError) {
|
168 | this.logger.error(parseError);
|
169 | this.logger.error("could not convert API response to JSON, above error is ignored and raw API response is returned to client");
|
170 | this.logger.error("Raw Error message from API ");
|
171 | this.logger.error("\"" + data + "\"");
|
172 |
|
173 | error = {
|
174 | status: status,
|
175 | message: "The API response could not be parsed.",
|
176 | body: data.join(""),
|
177 | parseError: parseError
|
178 | };
|
179 | }
|
180 |
|
181 | if (error) {
|
182 | error.statusCode = status;
|
183 | error.headers = headers;
|
184 | }
|
185 |
|
186 | if (typeof callback === "function") {
|
187 | callback(error, response);
|
188 | }
|
189 | }
|
190 | }, {
|
191 | key: "_addLimitedAccessMessageToErrors",
|
192 | value: function _addLimitedAccessMessageToErrors(callback, limitedAccessStatus) {
|
193 | return function (err, data) {
|
194 | if (err && err.status == limitedAccessStatus) {
|
195 | err._INFO_ = "This endpoint may need activating on your account. Please email support@nexmo.com for more information";
|
196 | }
|
197 |
|
198 | return callback(err, data);
|
199 | };
|
200 | }
|
201 | }, {
|
202 | key: "get",
|
203 | value: function get(path, params, callback) {
|
204 | var useJwt = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
205 | var useBasicAuth = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
206 |
|
207 | if (!callback) {
|
208 | if (typeof params == "function") {
|
209 | callback = params;
|
210 | params = {};
|
211 | }
|
212 | }
|
213 |
|
214 | params = params || {};
|
215 | if (!useJwt && !useBasicAuth) {
|
216 | params["api_key"] = this.credentials.apiKey;
|
217 | params["api_secret"] = this.credentials.apiSecret;
|
218 | }
|
219 |
|
220 | path = path + "?" + querystring.stringify(params);
|
221 |
|
222 | var headers = { "Content-Type": "application/json" };
|
223 | if (useJwt) {
|
224 | headers["Authorization"] = "Bearer " + this.credentials.generateJwt();
|
225 | }
|
226 | if (useBasicAuth) {
|
227 | headers["Authorization"] = "Basic " + Buffer.from(this.credentials.apiKey + ":" + this.credentials.apiSecret).toString("base64");
|
228 | }
|
229 |
|
230 | this.request({ path: path, headers: headers }, "GET", callback);
|
231 | }
|
232 | }, {
|
233 | key: "delete",
|
234 | value: function _delete(path, callback, useJwt, useBasicAuth) {
|
235 | var params = {};
|
236 | if (!useJwt && !useBasicAuth) {
|
237 | params["api_key"] = this.credentials.apiKey;
|
238 | params["api_secret"] = this.credentials.apiSecret;
|
239 | }
|
240 |
|
241 | var headers = {};
|
242 |
|
243 | if (useBasicAuth) {
|
244 | headers["Authorization"] = "Basic " + Buffer.from(this.credentials.apiKey + ":" + this.credentials.apiSecret).toString("base64");
|
245 | }
|
246 | path = path + "?" + querystring.stringify(params);
|
247 |
|
248 | this.request({ path: path, headers: headers }, "DELETE", callback);
|
249 | }
|
250 | }, {
|
251 | key: "postFile",
|
252 | value: function postFile(path, options, callback, useJwt) {
|
253 | var qs = {};
|
254 | if (!useJwt) {
|
255 | qs["api_key"] = this.credentials.apiKey;
|
256 | qs["api_secret"] = this.credentials.apiSecret;
|
257 | }
|
258 |
|
259 | if (Object.keys(qs).length) {
|
260 | var joinChar = "?";
|
261 | if (path.indexOf(joinChar) !== -1) {
|
262 | joinChar = "&";
|
263 | }
|
264 | path = path + joinChar + querystring.stringify(qs);
|
265 | }
|
266 |
|
267 | var file = options.file;
|
268 | delete options.file; // We don't send this as metadata
|
269 |
|
270 | var formData = {};
|
271 |
|
272 | if (file) {
|
273 | formData["filedata"] = {
|
274 | value: file,
|
275 | options: {
|
276 | filename: options.filename || null
|
277 | }
|
278 | };
|
279 | }
|
280 |
|
281 | if (options.info) {
|
282 | formData.info = JSON.stringify(options.info);
|
283 | }
|
284 |
|
285 | if (options.url) {
|
286 | formData.url = options.url;
|
287 | }
|
288 |
|
289 | this.requestLib.post({
|
290 | url: "https://" + this.host + path,
|
291 | formData: formData,
|
292 | headers: {
|
293 | Authorization: "Bearer " + this.credentials.generateJwt()
|
294 | }
|
295 | }, callback);
|
296 | }
|
297 | }, {
|
298 | key: "post",
|
299 | value: function post(path, params, callback, useJwt) {
|
300 | var qs = {};
|
301 | if (!useJwt) {
|
302 | qs["api_key"] = this.credentials.apiKey;
|
303 | qs["api_secret"] = this.credentials.apiSecret;
|
304 | }
|
305 |
|
306 | var joinChar = "?";
|
307 | if (path.indexOf(joinChar) !== -1) {
|
308 | joinChar = "&";
|
309 | }
|
310 |
|
311 | path = path + joinChar + querystring.stringify(qs);
|
312 |
|
313 | this.request({ path: path, body: querystring.stringify(params) }, "POST", callback);
|
314 | }
|
315 | }, {
|
316 | key: "postJson",
|
317 | value: function postJson(path, params, callback, useJwt, useBasicAuth) {
|
318 | var qs = {};
|
319 | if (!useJwt && !useBasicAuth) {
|
320 | qs["api_key"] = this.credentials.apiKey;
|
321 | qs["api_secret"] = this.credentials.apiSecret;
|
322 | }
|
323 |
|
324 | var joinChar = "?";
|
325 | if (path.indexOf(joinChar) !== -1) {
|
326 | joinChar = "&";
|
327 | }
|
328 |
|
329 | path = path + joinChar + querystring.stringify(qs);
|
330 |
|
331 | var headers = { "Content-Type": "application/json" };
|
332 | if (useBasicAuth) {
|
333 | headers["Authorization"] = "Basic " + Buffer.from(this.credentials.apiKey + ":" + this.credentials.apiSecret).toString("base64");
|
334 | }
|
335 |
|
336 | this.request({
|
337 | path: path,
|
338 | body: JSON.stringify(params),
|
339 | headers: headers
|
340 | }, "POST", callback);
|
341 | }
|
342 | }, {
|
343 | key: "postUseQueryString",
|
344 | value: function postUseQueryString(path, params, callback, useJwt) {
|
345 | params = params || {};
|
346 | if (!useJwt) {
|
347 | params["api_key"] = this.credentials.apiKey;
|
348 | params["api_secret"] = this.credentials.apiSecret;
|
349 | }
|
350 |
|
351 | path = path + "?" + querystring.stringify(params);
|
352 |
|
353 | this.request({ path: path }, "POST", callback);
|
354 | }
|
355 | }]);
|
356 |
|
357 | return HttpClient;
|
358 | }();
|
359 |
|
360 | exports.default = HttpClient;
|
361 | module.exports = exports["default"];
|
362 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9IdHRwQ2xpZW50LmpzIl0sIm5hbWVzIjpbImh0dHBzIiwicmVxdWlyZSIsImh0dHAiLCJyZXF1ZXN0IiwicXVlcnlzdHJpbmciLCJIdHRwQ2xpZW50Iiwib3B0aW9ucyIsImNyZWRlbnRpYWxzIiwiaG9zdCIsInBvcnQiLCJoZWFkZXJzIiwiQWNjZXB0IiwibG9nZ2VyIiwidGltZW91dCIsInJlcXVlc3RMaWIiLCJ1c2VyQWdlbnQiLCJlbmRwb2ludCIsIm1ldGhvZCIsImNhbGxiYWNrIiwic2tpcEpzb25QYXJzaW5nIiwicGF0aCIsIk9iamVjdCIsImFzc2lnbiIsInVuZGVmaW5lZCIsImtleXMiLCJmb3JFYWNoIiwia2V5IiwiaW5mbyIsImJvZHkiLCJlbmQiLCJyZXNwb25zZURhdGEiLCJvbiIsImlzQmluYXJ5IiwicmVzcG9uc2UiLCJzZXRFbmNvZGluZyIsInB1c2giLCJjaHVuayIsInN0YXR1c0NvZGUiLCJCdWZmZXIiLCJjb25jYXQiLCJfX3BhcnNlUmVzcG9uc2UiLCJlIiwiZXJyb3IiLCJodHRwUmVzcG9uc2UiLCJkYXRhIiwiaXNBcnJheU9yQnVmZmVyIiwiQXJyYXkiLCJFcnJvciIsInN0YXR1cyIsIm1lc3NhZ2UiLCJyZXRyeUFmdGVyTWlsbGlzIiwiam9pbiIsIkpTT04iLCJwYXJzZSIsInBhcnNlRXJyb3IiLCJsaW1pdGVkQWNjZXNzU3RhdHVzIiwiZXJyIiwiX0lORk9fIiwicGFyYW1zIiwidXNlSnd0IiwidXNlQmFzaWNBdXRoIiwiYXBpS2V5IiwiYXBpU2VjcmV0Iiwic3RyaW5naWZ5IiwiZ2VuZXJhdGVKd3QiLCJmcm9tIiwidG9TdHJpbmciLCJxcyIsImxlbmd0aCIsImpvaW5DaGFyIiwiaW5kZXhPZiIsImZpbGUiLCJmb3JtRGF0YSIsInZhbHVlIiwiZmlsZW5hbWUiLCJ1cmwiLCJwb3N0IiwiQXV0aG9yaXphdGlvbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBLElBQUlBLFFBQVFDLFFBQVEsT0FBUixDQUFa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QUVrQkUsSSxFQUFNc0MsTSxFQUFReEMsUSxFQUFVeUMsTSxFQUFRO0FBQ2pERCxlQUFTQSxVQUFVLEVBQW5CO0FBQ0EsVUFBSSxDQUFDQyxNQUFMLEVBQWE7QUFDWEQsZUFBTyxTQUFQLElBQW9CLEtBQUtuRCxXQUFMLENBQWlCc0QsTUFBckM7QUFDQUgsZUFBTyxZQUFQLElBQXVCLEtBQUtuRCxXQUFMLENBQWlCdUQsU0FBeEM7QUFDRDs7QUFFRDFDLGFBQU9BLE9BQU8sR0FBUCxHQUFhaEIsWUFBWTJELFNBQVosQ0FBc0JMLE1BQXRCLENBQXBCOztBQUVBLFdBQUt2RCxPQUFMLENBQWEsRUFBRWlCLE1BQU1BLElBQVIsRUFBYixFQUE2QixNQUE3QixFQUFxQ0YsUUFBckM7QUFDRDs7Ozs7O2tCQUdZYixVIiwiZmlsZSI6Ikh0dHBDbGllbnQuanMiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgaHR0cHMgPSByZXF1aXJlKFwiaHR0cHNcIik7XG52YXIgaHR0cCA9IHJlcXVpcmUoXCJodHRwXCIpO1xudmFyIHJlcXVlc3QgPSByZXF1aXJlKFwicmVxdWVzdFwiKTtcbnZhciBxdWVyeXN0cmluZyA9IHJlcXVpcmUoXCJxdWVyeXN0cmluZ1wiKTtcblxuY2xhc3MgSHR0cENsaWVudCB7XG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMsIGNyZWRlbnRpYWxzKSB7XG4gICAgdGhpcy5jcmVkZW50aWFscyA9IGNyZWRlbnRpYWxzO1xuICAgIHRoaXMuaG9zdCA9IG9wdGlvbnMuaG9zdCB8fCBcInJlc3QubmV4bW8uY29tXCI7XG4gICAgdGhpcy5wb3J0ID0gb3B0aW9ucy5wb3J0IHx8IDQ0MztcbiAgICB0aGlzLmh0dHBzID0gb3B0aW9ucy5odHRwcyB8fCBodHRwcztcbiAgICB0aGlzLmh0dHAgPSBvcHRpb25zLmh0dHAgfHwgaHR0cDtcbiAgICB0aGlzLmhlYWRlcnMgPSB7XG4gICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFwiLFxuICAgICAgQWNjZXB0OiBcImFwcGxpY2F0aW9uL2pzb25cIlxuICAgIH07XG4gICAgdGhpcy5sb2dnZXIgPSBvcHRpb25zLmxvZ2dlcjtcbiAgICB0aGlzLnRpbWVvdXQgPSBvcHRpb25zLnRpbWVvdXQ7XG4gICAgdGhpcy5yZXF1ZXN0TGliID0gcmVxdWVzdDtcblxuICAgIGlmIChvcHRpb25zLnVzZXJBZ2VudCkge1xuICAgICAgdGhpcy5oZWFkZXJzW1wiVXNlci1BZ2VudFwiXSA9IG9wdGlvbnMudXNlckFnZW50O1xuICAgIH1cbiAgfVxuXG4gIHJlcXVlc3QoZW5kcG9pbnQsIG1ldGhvZCwgY2FsbGJhY2ssIHNraXBKc29uUGFyc2luZyA9IGZhbHNlKSB7XG4gICAgaWYgKHR5cGVvZiBtZXRob2QgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgY2FsbGJhY2sgPSBtZXRob2Q7XG4gICAgICBlbmRwb2ludC5tZXRob2QgPSBlbmRwb2ludC5tZXRob2QgfHwgXCJHRVRcIjtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBtZXRob2QgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAgIGVuZHBvaW50Lm1ldGhvZCA9IG1ldGhvZDtcbiAgICB9XG5cbiAgICBpZiAoZW5kcG9pbnQubWV0aG9kID09PSBcIlBPU1RcIiB8fCBlbmRwb2ludC5tZXRob2QgPT09IFwiREVMRVRFXCIpIHtcbiAgICAgIC8vIFRPRE86IHZlcmlmeSB0aGUgZm9sbG93aW5nIGZpeCBpcyByZXF1aXJlZFxuICAgICAgLy8gRml4IGJyb2tlbiBkdWUgb3QgNDExIENvbnRlbnQtTGVuZ3RoIGVycm9yIG5vdyBzZW50IGJ5IE5leG1vIEFQSVxuICAgICAgLy8gUEwgMjAxNi1TZXB0LTYgLSBjb21tZW50ZWQgb3V0IENvbnRlbnQtTGVuZ3RoIDBcbiAgICAgIC8vIGhlYWRlcnNbJ0NvbnRlbnQtTGVuZ3RoJ10gPSAwO1xuICAgIH1cbiAgICB2YXIgb3B0aW9ucyA9IHtcbiAgICAgIGhvc3Q6IGVuZHBvaW50Lmhvc3QgPyBlbmRwb2ludC5ob3N0IDogdGhpcy5ob3N0LFxuICAgICAgcG9ydDogdGhpcy5wb3J0LFxuICAgICAgcGF0aDogZW5kcG9pbnQucGF0aCxcbiAgICAgIG1ldGhvZDogZW5kcG9pbnQubWV0aG9kLFxuICAgICAgaGVhZGVyczogT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5oZWFkZXJzKVxuICAgIH07XG5cbiAgICBpZiAodGhpcy50aW1lb3V0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIG9wdGlvbnMudGltZW91dCA9IHRoaXMudGltZW91dDtcbiAgICB9XG5cbiAgICAvLyBBbGxvdyBleGlzdGluZyBoZWFkZXJzIHRvIGJlIG92ZXJyaWRkZW5cbiAgICAvLyBBbGxvdyBuZXcgaGVhZGVycyB0byBiZSBhZGRlZFxuICAgIGlmIChlbmRwb2ludC5oZWFkZXJzKSB7XG4gICAgICBPYmplY3Qua2V5cyhlbmRwb2ludC5oZWFkZXJzKS5mb3JFYWNoKGZ1bmN0aW9uKGtleSkge1xuICAgICAgICBvcHRpb25zLmhlYWRlcnNba2V5XSA9IGVuZHBvaW50LmhlYWRlcnNba2V5XTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMubG9nZ2VyLmluZm8oXCJSZXF1ZXN0OlwiLCBvcHRpb25zLCBcIlxcbkJvZHk6XCIsIGVuZHBvaW50LmJvZHkpO1xuICAgIHZhciByZXF1ZXN0O1xuXG4gICAgaWYgKG9wdGlvbnMucG9ydCA9PT0gNDQzKSB7XG4gICAgICByZXF1ZXN0ID0gdGhpcy5odHRwcy5yZXF1ZXN0KG9wdGlvbnMpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXF1ZXN0ID0gdGhpcy5odHRwLnJlcXVlc3Qob3B0aW9ucyk7XG4gICAgfVxuXG4gICAgcmVxdWVzdC5lbmQoZW5kcG9pbnQuYm9keSk7XG5cbiAgICAvLyBLZWVwIGFuIGFycmF5IG9mIFN0cmluZyBvciBCdWZmZXJzLFxuICAgIC8vIGRlcGVuZGluZyBvbiBjb250ZW50IHR5cGUgKGJpbmFyeSBvciBKU09OKSBvZiByZXNwb25zZVxuICAgIHZhciByZXNwb25zZURhdGEgPSBbXTtcblxuICAgIHJlcXVlc3Qub24oXCJyZXNwb25zZVwiLCByZXNwb25zZSA9PiB7XG4gICAgICB2YXIgaXNCaW5hcnkgPVxuICAgICAgICByZXNwb25zZS5oZWFkZXJzW1wiY29udGVudC10eXBlXCJdID09PSBcImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiO1xuICAgICAgaWYgKCFpc0JpbmFyeSkge1xuICAgICAgICByZXNwb25zZS5zZXRFbmNvZGluZyhcInV0ZjhcIik7XG4gICAgICB9XG5cbiAgICAgIHJlc3BvbnNlLm9uKFwiZGF0YVwiLCBjaHVuayA9PiB7XG4gICAgICAgIHJlc3BvbnNlRGF0YS5wdXNoKGNodW5rKTtcbiAgICAgIH0pO1xuXG4gICAgICByZXNwb25zZS5vbihcImVuZFwiLCAoKSA9PiB7XG4gICAgICAgIHRoaXMubG9nZ2VyLmluZm8oXCJyZXNwb25zZSBlbmRlZDpcIiwgcmVzcG9uc2Uuc3RhdHVzQ29kZSk7XG4gICAgICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgICAgIGlmIChpc0JpbmFyeSkge1xuICAgICAgICAgICAgcmVzcG9uc2VEYXRhID0gQnVmZmVyLmNvbmNhdChyZXNwb25zZURhdGEpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMuX19wYXJzZVJlc3BvbnNlKFxuICAgICAgICAgICAgcmVzcG9uc2UsXG4gICAgICAgICAgICByZXNwb25zZURhdGEsXG4gICAgICAgICAgICBlbmRwb2ludC5tZXRob2QsXG4gICAgICAgICAgICBjYWxsYmFjayxcbiAgICAgICAgICAgIHNraXBKc29uUGFyc2luZ1xuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgcmVzcG9uc2Uub24oXCJjbG9zZVwiLCBlID0+IHtcbiAgICAgICAgaWYgKGUpIHtcbiAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihcbiAgICAgICAgICAgIFwicHJvYmxlbSB3aXRoIEFQSSByZXF1ZXN0IGRldGFpbGVkIHN0YWNrdHJhY2UgYmVsb3cgXCJcbiAgICAgICAgICApO1xuICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGUpO1xuICAgICAgICAgIGNhbGxiYWNrKGUpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcbiAgICByZXF1ZXN0Lm9uKFwiZXJyb3JcIiwgZSA9PiB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcihcInByb2JsZW0gd2l0aCBBUEkgcmVxdWVzdCBkZXRhaWxlZCBzdGFja3RyYWNlIGJlbG93IFwiKTtcbiAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGUpO1xuICAgICAgY2FsbGJhY2soZSk7XG4gICAgfSk7XG4gIH1cblxuICBfX3BhcnNlUmVzcG9uc2UoaHR0cFJlc3BvbnNlLCBkYXRhLCBtZXRob2QsIGNhbGxiYWNrLCBza2lwSnNvblBhcnNpbmcpIHtcbiAgICBjb25zdCBpc0FycmF5T3JCdWZmZXIgPSBkYXRhIGluc3RhbmNlb2YgQXJyYXkgfHwgZGF0YSBpbnN0YW5jZW9mIEJ1ZmZlcjtcbiAgICBpZiAoIWlzQXJyYXlPckJ1ZmZlcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiZGF0YSBzaG91bGQgYmUgb2YgdHlwZSBBcnJheSBvciBCdWZmZXJcIik7XG4gICAgfVxuXG4gICAgY29uc3Qgc3RhdHVzID0gaHR0cFJlc3BvbnNlLnN0YXR1c0NvZGU7XG4gICAgY29uc3QgaGVhZGVycyA9IGh0dHBSZXNwb25zZS5oZWFkZXJzO1xuXG4gICAgbGV0IHJlc3BvbnNlID0gbnVsbDtcbiAgICB2YXIgZXJyb3IgPSBudWxsO1xuXG4gICAgdHJ5IHtcbiAgICAgIGlmIChzdGF0dXMgPj0gNTAwKSB7XG4gICAgICAgIGVycm9yID0geyBtZXNzYWdlOiBcIlNlcnZlciBFcnJvclwiLCBzdGF0dXNDb2RlOiBzdGF0dXMgfTtcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGh0dHBSZXNwb25zZS5oZWFkZXJzW1wiY29udGVudC10eXBlXCJdID09PSBcImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiXG4gICAgICApIHtcbiAgICAgICAgcmVzcG9uc2UgPSBkYXRhO1xuICAgICAgfSBlbHNlIGlmIChzdGF0dXMgPT09IDQyOSkge1xuICAgICAgICAvLyA0MjkgZG9lcyBub3QgcmV0dXJuIGEgcGFyc2FibGUgYm9keVxuICAgICAgICBpZiAoIWhlYWRlcnNbXCJyZXRyeS1hZnRlclwiXSkge1xuICAgICAgICAgIC8vIHJldHJ5IGJhc2VkIG9uIGFsbG93ZWQgcGVyIHNlY29uZFxuICAgICAgICAgIGNvbnN0IHJldHJ5QWZ0ZXJNaWxsaXMgPSBtZXRob2QgPT09IFwiUE9TVFwiID8gMTAwMCAvIDIgOiAxMDAwIC8gNTtcbiAgICAgICAgICBoZWFkZXJzW1wicmV0cnktYWZ0ZXJcIl0gPSByZXRyeUFmdGVyTWlsbGlzO1xuICAgICAgICB9XG4gICAgICAgIGVycm9yID0geyBib2R5OiBkYXRhLmpvaW4oXCJcIikgfTtcbiAgICAgIH0gZWxzZSBpZiAoc3RhdHVzID09PSAyMDQpIHtcbiAgICAgICAgcmVzcG9uc2UgPSBudWxsO1xuICAgICAgfSBlbHNlIGlmIChzdGF0dXMgPj0gNDAwIHx8IHN0YXR1cyA8IDIwMCkge1xuICAgICAgICBlcnJvciA9IHsgYm9keTogSlNPTi5wYXJzZShkYXRhLmpvaW4oXCJcIikpLCBoZWFkZXJzIH07XG4gICAgICB9IGVsc2UgaWYgKG1ldGhvZCAhPT0gXCJERUxFVEVcIikge1xuICAgICAgICBpZiAoISFza2lwSnNvblBhcnNpbmcpIHtcbiAgICAgICAgICByZXNwb25zZSA9IGRhdGEuam9pbihcIlwiKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXNwb25zZSA9IEpTT04ucGFyc2UoZGF0YS5qb2luKFwiXCIpKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzcG9uc2UgPSBkYXRhO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKHBhcnNlRXJyb3IpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmVycm9yKHBhcnNlRXJyb3IpO1xuICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoXG4gICAgICAgIFwiY291bGQgbm90IGNvbnZlcnQgQVBJIHJlc3BvbnNlIHRvIEpTT04sIGFib3ZlIGVycm9yIGlzIGlnbm9yZWQgYW5kIHJhdyBBUEkgcmVzcG9uc2UgaXMgcmV0dXJuZWQgdG8gY2xpZW50XCJcbiAgICAgICk7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcihcIlJhdyBFcnJvciBtZXNzYWdlIGZyb20gQVBJIFwiKTtcbiAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBcIiR7ZGF0YX1cImApO1xuXG4gICAgICBlcnJvciA9IHtcbiAgICAgICAgc3RhdHVzOiBzdGF0dXMsXG4gICAgICAgIG1lc3NhZ2U6IFwiVGhlIEFQSSByZXNwb25zZSBjb3VsZCBub3QgYmUgcGFyc2VkLlwiLFxuICAgICAgICBib2R5OiBkYXRhLmpvaW4oXCJcIiksXG4gICAgICAgIHBhcnNlRXJyb3I6IHBhcnNlRXJyb3JcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKGVycm9yKSB7XG4gICAgICBlcnJvci5zdGF0dXNDb2RlID0gc3RhdHVzO1xuICAgICAgZXJyb3IuaGVhZGVycyA9IGhlYWRlcnM7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBjYWxsYmFjayA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICBjYWxsYmFjayhlcnJvciwgcmVzcG9uc2UpO1xuICAgIH1cbiAgfVxuXG4gIF9hZGRMaW1pdGVkQWNjZXNzTWVzc2FnZVRvRXJyb3JzKGNhbGxiYWNrLCBsaW1pdGVkQWNjZXNzU3RhdHVzKSB7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKGVyciwgZGF0YSkge1xuICAgICAgaWYgKGVyciAmJiBlcnIuc3RhdHVzID09IGxpbWl0ZWRBY2Nlc3NTdGF0dXMpIHtcbiAgICAgICAgZXJyLl9JTkZPXyA9XG4gICAgICAgICAgXCJUaGlzIGVuZHBvaW50IG1heSBuZWVkIGFjdGl2YXRpbmcgb24geW91ciBhY2NvdW50LiBQbGVhc2UgZW1haWwgc3VwcG9ydEBuZXhtby5jb20gZm9yIG1vcmUgaW5mb3JtYXRpb25cIjtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGNhbGxiYWNrKGVyciwgZGF0YSk7XG4gICAgfTtcbiAgfVxuXG4gIGdldChwYXRoLCBwYXJhbXMsIGNhbGxiYWNrLCB1c2VKd3QgPSBmYWxzZSwgdXNlQmFzaWNBdXRoID0gZmFsc2UpIHtcbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICBpZiAodHlwZW9mIHBhcmFtcyA9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY2FsbGJhY2sgPSBwYXJhbXM7XG4gICAgICAgIHBhcmFtcyA9IHt9O1xuICAgICAgfVxuICAgIH1cblxuICAgIHBhcmFtcyA9IHBhcmFtcyB8fCB7fTtcbiAgICBpZiAoIXVzZUp3dCAmJiAhdXNlQmFzaWNBdXRoKSB7XG4gICAgICBwYXJhbXNbXCJhcGlfa2V5XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlLZXk7XG4gICAgICBwYXJhbXNbXCJhcGlfc2VjcmV0XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlTZWNyZXQ7XG4gICAgfVxuXG4gICAgcGF0aCA9IHBhdGggKyBcIj9cIiArIHF1ZXJ5c3RyaW5nLnN0cmluZ2lmeShwYXJhbXMpO1xuXG4gICAgY29uc3QgaGVhZGVycyA9IHsgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIgfTtcbiAgICBpZiAodXNlSnd0KSB7XG4gICAgICBoZWFkZXJzW1wiQXV0aG9yaXphdGlvblwiXSA9IGBCZWFyZXIgJHt0aGlzLmNyZWRlbnRpYWxzLmdlbmVyYXRlSnd0KCl9YDtcbiAgICB9XG4gICAgaWYgKHVzZUJhc2ljQXV0aCkge1xuICAgICAgaGVhZGVyc1tcIkF1dGhvcml6YXRpb25cIl0gPSBgQmFzaWMgJHtCdWZmZXIuZnJvbShcbiAgICAgICAgdGhpcy5jcmVkZW50aWFscy5hcGlLZXkgKyBcIjpcIiArIHRoaXMuY3JlZGVudGlhbHMuYXBpU2VjcmV0XG4gICAgICApLnRvU3RyaW5nKFwiYmFzZTY0XCIpfWA7XG4gICAgfVxuXG4gICAgdGhpcy5yZXF1ZXN0KHsgcGF0aDogcGF0aCwgaGVhZGVycyB9LCBcIkdFVFwiLCBjYWxsYmFjayk7XG4gIH1cblxuICBkZWxldGUocGF0aCwgY2FsbGJhY2ssIHVzZUp3dCwgdXNlQmFzaWNBdXRoKSB7XG4gICAgbGV0IHBhcmFtcyA9IHt9O1xuICAgIGlmICghdXNlSnd0ICYmICF1c2VCYXNpY0F1dGgpIHtcbiAgICAgIHBhcmFtc1tcImFwaV9rZXlcIl0gPSB0aGlzLmNyZWRlbnRpYWxzLmFwaUtleTtcbiAgICAgIHBhcmFtc1tcImFwaV9zZWNyZXRcIl0gPSB0aGlzLmNyZWRlbnRpYWxzLmFwaVNlY3JldDtcbiAgICB9XG5cbiAgICBsZXQgaGVhZGVycyA9IHt9O1xuXG4gICAgaWYgKHVzZUJhc2ljQXV0aCkge1xuICAgICAgaGVhZGVyc1tcIkF1dGhvcml6YXRpb25cIl0gPSBgQmFzaWMgJHtCdWZmZXIuZnJvbShcbiAgICAgICAgdGhpcy5jcmVkZW50aWFscy5hcGlLZXkgKyBcIjpcIiArIHRoaXMuY3JlZGVudGlhbHMuYXBpU2VjcmV0XG4gICAgICApLnRvU3RyaW5nKFwiYmFzZTY0XCIpfWA7XG4gICAgfVxuICAgIHBhdGggPSBwYXRoICsgXCI/XCIgKyBxdWVyeXN0cmluZy5zdHJpbmdpZnkocGFyYW1zKTtcblxuICAgIHRoaXMucmVxdWVzdCh7IHBhdGg6IHBhdGgsIGhlYWRlcnMgfSwgXCJERUxFVEVcIiwgY2FsbGJhY2spO1xuICB9XG5cbiAgcG9zdEZpbGUocGF0aCwgb3B0aW9ucywgY2FsbGJhY2ssIHVzZUp3dCkge1xuICAgIGxldCBxcyA9IHt9O1xuICAgIGlmICghdXNlSnd0KSB7XG4gICAgICBxc1tcImFwaV9rZXlcIl0gPSB0aGlzLmNyZWRlbnRpYWxzLmFwaUtleTtcbiAgICAgIHFzW1wiYXBpX3NlY3JldFwiXSA9IHRoaXMuY3JlZGVudGlhbHMuYXBpU2VjcmV0O1xuICAgIH1cblxuICAgIGlmIChPYmplY3Qua2V5cyhxcykubGVuZ3RoKSB7XG4gICAgICBsZXQgam9pbkNoYXIgPSBcIj9cIjtcbiAgICAgIGlmIChwYXRoLmluZGV4T2Yoam9pbkNoYXIpICE9PSAtMSkge1xuICAgICAgICBqb2luQ2hhciA9IFwiJlwiO1xuICAgICAgfVxuICAgICAgcGF0aCA9IHBhdGggKyBqb2luQ2hhciArIHF1ZXJ5c3RyaW5nLnN0cmluZ2lmeShxcyk7XG4gICAgfVxuXG4gICAgY29uc3QgZmlsZSA9IG9wdGlvbnMuZmlsZTtcbiAgICBkZWxldGUgb3B0aW9ucy5maWxlOyAvLyBXZSBkb24ndCBzZW5kIHRoaXMgYXMgbWV0YWRhdGFcblxuICAgIGNvbnN0IGZvcm1EYXRhID0ge307XG5cbiAgICBpZiAoZmlsZSkge1xuICAgICAgZm9ybURhdGFbXCJmaWxlZGF0YVwiXSA9IHtcbiAgICAgICAgdmFsdWU6IGZpbGUsXG4gICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICBmaWxlbmFtZTogb3B0aW9ucy5maWxlbmFtZSB8fCBudWxsXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKG9wdGlvbnMuaW5mbykge1xuICAgICAgZm9ybURhdGEuaW5mbyA9IEpTT04uc3RyaW5naWZ5KG9wdGlvbnMuaW5mbyk7XG4gICAgfVxuXG4gICAgaWYgKG9wdGlvbnMudXJsKSB7XG4gICAgICBmb3JtRGF0YS51cmwgPSBvcHRpb25zLnVybDtcbiAgICB9XG5cbiAgICB0aGlzLnJlcXVlc3RMaWIucG9zdChcbiAgICAgIHtcbiAgICAgICAgdXJsOiBcImh0dHBzOi8vXCIgKyB0aGlzLmhvc3QgKyBwYXRoLFxuICAgICAgICBmb3JtRGF0YTogZm9ybURhdGEsXG4gICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICBBdXRob3JpemF0aW9uOiBgQmVhcmVyICR7dGhpcy5jcmVkZW50aWFscy5nZW5lcmF0ZUp3dCgpfWBcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGNhbGxiYWNrXG4gICAgKTtcbiAgfVxuXG4gIHBvc3QocGF0aCwgcGFyYW1zLCBjYWxsYmFjaywgdXNlSnd0KSB7XG4gICAgbGV0IHFzID0ge307XG4gICAgaWYgKCF1c2VKd3QpIHtcbiAgICAgIHFzW1wiYXBpX2tleVwiXSA9IHRoaXMuY3JlZGVudGlhbHMuYXBpS2V5O1xuICAgICAgcXNbXCJhcGlfc2VjcmV0XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlTZWNyZXQ7XG4gICAgfVxuXG4gICAgbGV0IGpvaW5DaGFyID0gXCI/XCI7XG4gICAgaWYgKHBhdGguaW5kZXhPZihqb2luQ2hhcikgIT09IC0xKSB7XG4gICAgICBqb2luQ2hhciA9IFwiJlwiO1xuICAgIH1cblxuICAgIHBhdGggPSBwYXRoICsgam9pbkNoYXIgKyBxdWVyeXN0cmluZy5zdHJpbmdpZnkocXMpO1xuXG4gICAgdGhpcy5yZXF1ZXN0KFxuICAgICAgeyBwYXRoOiBwYXRoLCBib2R5OiBxdWVyeXN0cmluZy5zdHJpbmdpZnkocGFyYW1zKSB9LFxuICAgICAgXCJQT1NUXCIsXG4gICAgICBjYWxsYmFja1xuICAgICk7XG4gIH1cblxuICBwb3N0SnNvbihwYXRoLCBwYXJhbXMsIGNhbGxiYWNrLCB1c2VKd3QsIHVzZUJhc2ljQXV0aCkge1xuICAgIGxldCBxcyA9IHt9O1xuICAgIGlmICghdXNlSnd0ICYmICF1c2VCYXNpY0F1dGgpIHtcbiAgICAgIHFzW1wiYXBpX2tleVwiXSA9IHRoaXMuY3JlZGVudGlhbHMuYXBpS2V5O1xuICAgICAgcXNbXCJhcGlfc2VjcmV0XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlTZWNyZXQ7XG4gICAgfVxuXG4gICAgbGV0IGpvaW5DaGFyID0gXCI/XCI7XG4gICAgaWYgKHBhdGguaW5kZXhPZihqb2luQ2hhcikgIT09IC0xKSB7XG4gICAgICBqb2luQ2hhciA9IFwiJlwiO1xuICAgIH1cblxuICAgIHBhdGggPSBwYXRoICsgam9pbkNoYXIgKyBxdWVyeXN0cmluZy5zdHJpbmdpZnkocXMpO1xuXG4gICAgbGV0IGhlYWRlcnMgPSB7IFwiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH07XG4gICAgaWYgKHVzZUJhc2ljQXV0aCkge1xuICAgICAgaGVhZGVyc1tcIkF1dGhvcml6YXRpb25cIl0gPSBgQmFzaWMgJHtCdWZmZXIuZnJvbShcbiAgICAgICAgdGhpcy5jcmVkZW50aWFscy5hcGlLZXkgKyBcIjpcIiArIHRoaXMuY3JlZGVudGlhbHMuYXBpU2VjcmV0XG4gICAgICApLnRvU3RyaW5nKFwiYmFzZTY0XCIpfWA7XG4gICAgfVxuXG4gICAgdGhpcy5yZXF1ZXN0KFxuICAgICAge1xuICAgICAgICBwYXRoOiBwYXRoLFxuICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXJhbXMpLFxuICAgICAgICBoZWFkZXJzXG4gICAgICB9LFxuICAgICAgXCJQT1NUXCIsXG4gICAgICBjYWxsYmFja1xuICAgICk7XG4gIH1cblxuICBwb3N0VXNlUXVlcnlTdHJpbmcocGF0aCwgcGFyYW1zLCBjYWxsYmFjaywgdXNlSnd0KSB7XG4gICAgcGFyYW1zID0gcGFyYW1zIHx8IHt9O1xuICAgIGlmICghdXNlSnd0KSB7XG4gICAgICBwYXJhbXNbXCJhcGlfa2V5XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlLZXk7XG4gICAgICBwYXJhbXNbXCJhcGlfc2VjcmV0XCJdID0gdGhpcy5jcmVkZW50aWFscy5hcGlTZWNyZXQ7XG4gICAgfVxuXG4gICAgcGF0aCA9IHBhdGggKyBcIj9cIiArIHF1ZXJ5c3RyaW5nLnN0cmluZ2lmeShwYXJhbXMpO1xuXG4gICAgdGhpcy5yZXF1ZXN0KHsgcGF0aDogcGF0aCB9LCBcIlBPU1RcIiwgY2FsbGJhY2spO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IEh0dHBDbGllbnQ7XG4iXX0= |
\ | No newline at end of file |