Add unique ids to the intercepted XHR objects to make the tracking correctly across inspector restarts.

Summary: In previous `XHRInterceptor`, it sometimes crashes when restarting the network inspector because the id of the XHR objects are not unique all time. Fix this in diff by adding a global id "generator" for all intercepted xhr objects in order to make it safe across inspector restarts.

Reviewed By: davidaurelio

Differential Revision: D3641624

fbshipit-source-id: f9a1589f278023243aa182d3da93ce69c985587c
This commit is contained in:
Siqi Liu 2016-08-02 08:23:53 -07:00 committed by Facebook Github Bot 5
parent f94e4348dd
commit 1e8b83d2e6
2 changed files with 78 additions and 35 deletions

View File

@ -24,6 +24,9 @@ const XHRInterceptor = require('XHRInterceptor');
const LISTVIEW_CELL_HEIGHT = 15;
const SEPARATOR_THICKNESS = 2;
// Global id for the intercepted XMLHttpRequest objects.
let nextXHRId = 0;
type NetworkRequestInfo = {
url?: string,
method?: string,
@ -61,6 +64,8 @@ class NetworkOverlay extends React.Component {
) => ReactElement<any>;
_renderScrollComponent: (props: Object) => ReactElement<any>;
_closeButtonClicked: () => void;
// Map of `xhr._index` -> `index in `_requests``.
_xhrIdMap: {[key: number]: number};
state: {
dataSource: ListView.DataSource,
@ -87,6 +92,7 @@ class NetworkOverlay extends React.Component {
this._renderRow = this._renderRow.bind(this);
this._renderScrollComponent = this._renderScrollComponent.bind(this);
this._closeButtonClicked = this._closeButtonClicked.bind(this);
this._xhrIdMap = {};
}
_enableInterception(): void {
@ -95,13 +101,20 @@ class NetworkOverlay extends React.Component {
}
// Show the network request item in listView as soon as it was opened.
XHRInterceptor.setOpenCallback(function(method, url, xhr) {
// Add one private `_index` property to identify the xhr object,
// Generate a global id for each intercepted xhr object, add this id
// to the xhr object as a private `_index` property to identify it,
// so that we can distinguish different xhr objects in callbacks.
xhr._index = this._requests.length;
const _xhr: NetworkRequestInfo = {'method': method, 'url': url};
xhr._index = nextXHRId++;
const xhrIndex = this._requests.length;
this._xhrIdMap[xhr._index] = xhrIndex;
const _xhr: NetworkRequestInfo = {
'method': method,
'url': url
};
this._requests.push(_xhr);
this._detailViewItems.push([]);
this._genDetailViewItem(xhr._index);
this._genDetailViewItem(xhrIndex);
this.setState(
{dataSource: this._listViewDataSource.cloneWithRows(this._requests)},
this._scrollToBottom(),
@ -109,35 +122,38 @@ class NetworkOverlay extends React.Component {
}.bind(this));
XHRInterceptor.setRequestHeaderCallback(function(header, value, xhr) {
if (xhr._index === undefined) {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhr._index];
const networkInfo = this._requests[xhrIndex];
if (!networkInfo.requestHeaders) {
networkInfo.requestHeaders = {};
}
networkInfo.requestHeaders[header] = value;
this._genDetailViewItem(xhr._index);
this._genDetailViewItem(xhrIndex);
}.bind(this));
XHRInterceptor.setSendCallback(function(data, xhr) {
if (xhr._index === undefined) {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
this._requests[xhr._index].dataSent = data;
this._genDetailViewItem(xhr._index);
this._requests[xhrIndex].dataSent = data;
this._genDetailViewItem(xhrIndex);
}.bind(this));
XHRInterceptor.setHeaderReceivedCallback(
function(type, size, responseHeaders, xhr) {
if (xhr._index === undefined) {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhr._index];
const networkInfo = this._requests[xhrIndex];
networkInfo.responseContentType = type;
networkInfo.responseSize = size;
networkInfo.responseHeaders = responseHeaders;
this._genDetailViewItem(xhr._index);
this._genDetailViewItem(xhrIndex);
}.bind(this)
);
@ -150,16 +166,17 @@ class NetworkOverlay extends React.Component {
responseType,
xhr,
) {
if (xhr._index === undefined) {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhr._index];
const networkInfo = this._requests[xhrIndex];
networkInfo.status = status;
networkInfo.timeout = timeout;
networkInfo.response = response;
networkInfo.responseURL = responseURL;
networkInfo.responseType = responseType;
this._genDetailViewItem(xhr._index);
this._genDetailViewItem(xhrIndex);
}.bind(this)
);
@ -296,11 +313,24 @@ class NetworkOverlay extends React.Component {
return JSON.stringify(value);
}
if (typeof value === 'string' && value.length > 500) {
return String(value).substr(0, 500).concat('\n***TRUNCATED TO 500 CHARACTERS***');
return String(value).substr(0, 500).concat(
'\n***TRUNCATED TO 500 CHARACTERS***');
}
return value;
}
_getRequestIndexByXHRID(index: number): number {
if (index === undefined) {
return -1;
}
const xhrIndex = this._xhrIdMap[index];
if (xhrIndex === undefined) {
return -1;
} else {
return xhrIndex;
}
}
/**
* Generate a list of views containing network request information for
* a XHR object, to be shown in the detail scrollview. This function
@ -324,7 +354,7 @@ class NetworkOverlay extends React.Component {
);
}
// Re-render if this network request is showing in the detail view.
if (this.state.detailRowID != null && this.state.detailRowID == index) {
if (this.state.detailRowID != null && Number(this.state.detailRowID) === index) {
this.setState({newDetailInfo: true});
}
}

View File

@ -77,23 +77,32 @@ const XHRInterceptor = {
// Override `open` method for all XHR requests to intercept the request
// method and url, then pass them through the `openCallback`.
XMLHttpRequest.prototype.open = function(method, url) {
openCallback(method, url, this);
if (openCallback) {
openCallback(method, url, this);
}
originalXHROpen.apply(this, arguments);
};
// Override `setRequestHeader` method for all XHR requests to intercept
// the request headers, then pass them through the `requestHeaderCallback`.
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
requestHeaderCallback(header, value, this);
if (requestHeaderCallback) {
requestHeaderCallback(header, value, this);
}
originalXHRSetRequestHeader.apply(this, arguments);
};
// Override `send` method of all XHR requests to intercept the data sent,
// register listeners to intercept the response, and invoke the callbacks.
XMLHttpRequest.prototype.send = function(data) {
sendCallback(data, this);
if (sendCallback) {
sendCallback(data, this);
}
if (this.addEventListener) {
this.addEventListener('readystatechange', () => {
if (!isInterceptorEnabled) {
return;
}
if (this.readyState === this.HEADERS_RECEIVED) {
const contentTypeString = this.getResponseHeader('Content-Type');
const contentLengthString =
@ -105,22 +114,26 @@ const XHRInterceptor = {
if (contentLengthString) {
responseSize = parseInt(contentLengthString, 10);
}
headerReceivedCallback(
responseContentType,
responseSize,
this.getAllResponseHeaders(),
this,
);
if (headerReceivedCallback) {
headerReceivedCallback(
responseContentType,
responseSize,
this.getAllResponseHeaders(),
this,
);
}
}
if (this.readyState === this.DONE) {
responseCallback(
this.status,
this.timeout,
this.response,
this.responseURL,
this.responseType,
this,
);
if (responseCallback) {
responseCallback(
this.status,
this.timeout,
this.response,
this.responseURL,
this.responseType,
this,
);
}
}
}, false);
}