@inst/vscode-bin-darwin
Version:
BINARY ONLY - VSCode binary deployment for macOS
219 lines (218 loc) • 9.43 kB
JavaScript
///<reference path="..\typings\globals\node\index.d.ts" />
;
var fs = require("fs");
var http = require("http");
var https = require("https");
var os = require("os");
var path = require("path");
var url = require("url");
var zlib = require("zlib");
var Logging = require("./Logging");
var AutoCollectClientRequests = require("../AutoCollection/ClientRequests");
var Sender = (function () {
function Sender(getUrl, onSuccess, onError) {
this._getUrl = getUrl;
this._onSuccess = onSuccess;
this._onError = onError;
this._enableOfflineMode = false;
this._resendInterval = Sender.WAIT_BETWEEN_RESEND;
}
/**
* Enable or disable offline mode
*/
Sender.prototype.setOfflineMode = function (value, resendInterval) {
this._enableOfflineMode = value;
if (typeof resendInterval === 'number' && resendInterval >= 0) {
this._resendInterval = Math.floor(resendInterval);
}
};
Sender.prototype.send = function (payload, callback) {
var _this = this;
var endpointUrl = this._getUrl();
if (endpointUrl && endpointUrl.indexOf("//") === 0) {
// use https if the config did not specify a protocol
endpointUrl = "https:" + endpointUrl;
}
// todo: investigate specifying an agent here: https://nodejs.org/api/http.html#http_class_http_agent
var parsedUrl = url.parse(endpointUrl);
var options = {
host: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
method: "POST",
headers: {
"Content-Type": "application/x-json-stream"
}
};
zlib.gzip(payload, function (err, buffer) {
var dataToSend = buffer;
if (err) {
Logging.warn(err);
dataToSend = payload; // something went wrong so send without gzip
options.headers["Content-Length"] = payload.length;
}
else {
options.headers["Content-Encoding"] = "gzip";
options.headers["Content-Length"] = buffer.length;
}
Logging.info(Sender.TAG, options);
// Ensure this request is not captured by auto-collection.
options[AutoCollectClientRequests.disableCollectionRequestOption] = true;
var requestCallback = function (res) {
res.setEncoding("utf-8");
//returns empty if the data is accepted
var responseString = "";
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
Logging.info(Sender.TAG, responseString);
if (typeof _this._onSuccess === "function") {
_this._onSuccess(responseString);
}
if (typeof callback === "function") {
callback(responseString);
}
if (_this._enableOfflineMode) {
// try to send any cached events if the user is back online
if (res.statusCode === 200) {
setTimeout(function () { return _this._sendFirstFileOnDisk(); }, _this._resendInterval);
}
else if (res.statusCode === 206 ||
res.statusCode === 429 ||
res.statusCode === 439) {
_this._storeToDisk(payload);
}
}
});
};
var req = (parsedUrl.protocol == "https:") ?
https.request(options, requestCallback) :
http.request(options, requestCallback);
req.on("error", function (error) {
// todo: handle error codes better (group to recoverable/non-recoverable and persist)
Logging.warn(Sender.TAG, error);
_this._onErrorHelper(error);
if (typeof callback === "function") {
var errorMessage = "error sending telemetry";
if (error && (typeof error.toString === "function")) {
errorMessage = error.toString();
}
callback(errorMessage);
}
if (_this._enableOfflineMode) {
_this._storeToDisk(payload);
}
});
req.write(dataToSend);
req.end();
});
};
Sender.prototype.saveOnCrash = function (payload) {
this._storeToDiskSync(payload);
};
Sender.prototype._confirmDirExists = function (direcotry, callback) {
fs.exists(direcotry, function (exists) {
if (!exists) {
fs.mkdir(direcotry, function (err) {
callback(err);
});
}
else {
callback(null);
}
});
};
/**
* Stores the payload as a json file on disk in the temp direcotry
*/
Sender.prototype._storeToDisk = function (payload) {
var _this = this;
//ensure directory is created
var direcotry = path.join(os.tmpdir(), Sender.TEMPDIR);
this._confirmDirExists(direcotry, function (error) {
if (error) {
_this._onErrorHelper(error);
return;
}
//create file - file name for now is the timestamp, a better approach would be a UUID but that
//would require an external dependency
var fileName = new Date().getTime() + ".ai.json";
var fileFullPath = path.join(direcotry, fileName);
Logging.info(Sender.TAG, "saving data to disk at: " + fileFullPath);
fs.writeFile(fileFullPath, payload, function (error) { return _this._onErrorHelper(error); });
});
};
/**
* Stores the payload as a json file on disk using sync file operations
* this is used when storing data before crashes
*/
Sender.prototype._storeToDiskSync = function (payload) {
var direcotry = path.join(os.tmpdir(), Sender.TEMPDIR);
try {
if (!fs.existsSync(direcotry)) {
fs.mkdirSync(direcotry);
}
//create file - file name for now is the timestamp, a better approach would be a UUID but that
//would require an external dependency
var fileName = new Date().getTime() + ".ai.json";
var fileFullPath = path.join(direcotry, fileName);
Logging.info(Sender.TAG, "saving data before crash to disk at: " + fileFullPath);
fs.writeFileSync(fileFullPath, payload);
}
catch (error) {
this._onErrorHelper(error);
}
};
/**
* Check for temp telemetry files
* reads the first file if exist, deletes it and tries to send its load
*/
Sender.prototype._sendFirstFileOnDisk = function () {
var _this = this;
var tempDir = path.join(os.tmpdir(), Sender.TEMPDIR);
fs.exists(tempDir, function (exists) {
if (exists) {
fs.readdir(tempDir, function (error, files) {
if (!error) {
files = files.filter(function (f) { return path.basename(f).indexOf(".ai.json") > -1; });
if (files.length > 0) {
var firstFile = files[0];
var filePath = path.join(tempDir, firstFile);
fs.readFile(filePath, function (error, payload) {
if (!error) {
// delete the file first to prevent double sending
fs.unlink(filePath, function (error) {
if (!error) {
_this.send(payload);
}
else {
_this._onErrorHelper(error);
}
});
}
else {
_this._onErrorHelper(error);
}
});
}
}
else {
_this._onErrorHelper(error);
}
});
}
});
};
Sender.prototype._onErrorHelper = function (error) {
if (typeof this._onError === "function") {
this._onError(error);
}
};
return Sender;
}());
Sender.TAG = "Sender";
// the amount of time the SDK will wait between resending cached data, this buffer is to avoid any throtelling from the service side
Sender.WAIT_BETWEEN_RESEND = 60 * 1000;
Sender.TEMPDIR = "appInsights-node";
module.exports = Sender;