atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
237 lines (189 loc) • 5.99 kB
JavaScript
var fs = require('fs');
var path = require('path');
var dataUri = require('strong-data-uri');
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var injection = require.resolve('./Injections/NetworkAgent');
function NetworkAgent(config, session) {
try {
this._noInject = config.inject === false || config.inject.network === false;
} catch (e) {
this._noInject = false;
}
this._debuggerClient = session.debuggerClient;
this._frontendClient = session.frontendClient;
this._injectorClient = session.injectorClient;
this._capturingEnabled = true;
this._dataStorage = {};
if (!this._noInject) this._injectorClient.on('inject', this._inject.bind(this));
}
NetworkAgent.prototype._inject = function() {
this._translateEventToFrontend(
'requestWillBeSent',
'responseReceived',
'dataReceived',
'loadingFinished',
'loadingFailed'
);
this._handleEvent('_requestWillBeSent', this._registerInDataStorage.bind(this));
this._handleEvent('_dataReceived', this._saveToDataStorage.bind(this));
this._handleEvent('_loadingFinished', this._constructRequestData.bind(this));
this._handleEvent('_loadingFailed', this._dumpRequestData.bind(this));
this._injectorClient.injection(
function(require, debug, options) {
require(options.injection)(require, debug, options);
},
{
injection: injection
},
function(error, result) {
this._injected = !error;
if (error) return this._frontendClient.sendLogToConsole('error', error.message || error);
}.bind(this)
);
};
/**
* @param {...string} eventNames
*/
NetworkAgent.prototype._translateEventToFrontend = function(eventNames) {
Array.prototype.forEach.call(arguments, function(event) {
event = 'Network.' + event;
this._debuggerClient.on(event, function(message) {
this._frontendClient.sendEvent(event, message);
}.bind(this));
}, this);
};
NetworkAgent.prototype._handleEvent = function(event, handle) {
event = 'Network.' + event;
this._debuggerClient.on(event, handle);
};
NetworkAgent.prototype._registerInDataStorage = function(message) {
if (!this._capturingEnabled) return;
var requestId = message.requestId;
if (this._dataStorage[requestId])
throw new Error('Data storage for request #' + requestId + ' already exists');
this._dataStorage[requestId] = new ResponseData();
};
NetworkAgent.prototype._saveToDataStorage = function(message) {
if (!this._capturingEnabled) return;
var responseData = this._dataStorage[message.requestId];
if (!responseData) return;
responseData.push(message.data);
};
NetworkAgent.prototype._constructRequestData = function(message) {
if (!this._capturingEnabled) return;
var responseData = this._dataStorage[message.requestId];
if (!responseData) return;
responseData.finish();
};
NetworkAgent.prototype._dumpRequestData = function(message) {
if (!this._capturingEnabled) return;
var responseData = this._dataStorage[message.requestId];
if (!responseData) return;
responseData.dump();
};
NetworkAgent.prototype.getResponseBody = function(params, done) {
var responseData = this._dataStorage[params.requestId];
if (!responseData)
return done(new Error('There is no data for request #' + params.requestId));
var result = {
base64Encoded: false,
body: null
};
if (!responseData.finished) {
responseData.once('finish', function(data) {
result.body = data;
done(null, result);
});
} else {
process.nextTick(function() {
result.body = responseData.data;
done(null, result);
});
}
};
NetworkAgent.prototype._setCapturingEnabled = function(params, done) {
this._capturingEnabled = params.enabled;
done();
};
NetworkAgent.prototype._clearCapturedData = function(params, done) {
Object.keys(this._dataStorage).forEach(function(key) {
this._dataStorage[key].dispose();
}, this);
this._dataStorage = {};
done();
};
NetworkAgent.prototype.loadResourceForFrontend = function(params, done) {
if (/^data:/.test(params.url)) {
try {
done(null, {
statusCode: 200,
headers: {},
content: dataUri.decode(params.url).toString('ascii')
});
} catch (err) {
done(err);
}
return;
}
loadFileResource(params, done);
};
function loadFileResource(params, done) {
var match = params.url.match(/^file:\/\/(.*)$/);
if (!match) {
return done(
'URL scheme not supported by Network.loadResourceForFrontend: ' +
params.url);
}
var filePath = match[1];
if (process.platform == 'win32') {
// On Windows, we should receive '/C:/path/to/file'.
if (!/^\/[a-zA-Z]:\//.test(filePath)) {
return done('Invalid windows path: ' + filePath);
}
// Remove leading '/' and replace all other '/' with '\'
filePath = filePath.slice(1).split('/').join(path.sep);
}
// ensure there are no ".." in the path
filePath = path.normalize(filePath);
fs.readFile(
filePath,
{ encoding: 'utf8' },
function(err, content) {
if (err) return done(err);
done(null, {
statusCode: 200,
headers: {},
content: content
});
}
);
}
NetworkAgent.prototype.setUserAgentOverride = function(params, done) {
done(null, {});
};
function ResponseData(id) {
this.data = [];
this.finished = false;
}
inherits(ResponseData, EventEmitter);
ResponseData.prototype.finish = function() {
if (this.finished) return;
this.data = this.data.join('');
this.finished = true;
this.emit('finish', this.data);
};
ResponseData.prototype.dump = function() {
if (this.finished) return;
this.data = '';
this.finished = true;
this.emit('finish', this.data);
};
ResponseData.prototype.push = function(chunk) {
this.data.push(chunk);
};
ResponseData.prototype.dispose = function() {
if (!this.finished) this.dump();
this.removeAllListeners();
};
exports.NetworkAgent = NetworkAgent;