Skip to content

Commit 1e8b83d

Browse files
Siqi LiuFacebook Github Bot 5
authored andcommitted
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
1 parent f94e434 commit 1e8b83d

File tree

2 files changed

+78
-35
lines changed

2 files changed

+78
-35
lines changed

Libraries/Inspector/NetworkOverlay.js

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const XHRInterceptor = require('XHRInterceptor');
2424
const LISTVIEW_CELL_HEIGHT = 15;
2525
const SEPARATOR_THICKNESS = 2;
2626

27+
// Global id for the intercepted XMLHttpRequest objects.
28+
let nextXHRId = 0;
29+
2730
type NetworkRequestInfo = {
2831
url?: string,
2932
method?: string,
@@ -61,6 +64,8 @@ class NetworkOverlay extends React.Component {
6164
) => ReactElement<any>;
6265
_renderScrollComponent: (props: Object) => ReactElement<any>;
6366
_closeButtonClicked: () => void;
67+
// Map of `xhr._index` -> `index in `_requests``.
68+
_xhrIdMap: {[key: number]: number};
6469

6570
state: {
6671
dataSource: ListView.DataSource,
@@ -87,6 +92,7 @@ class NetworkOverlay extends React.Component {
8792
this._renderRow = this._renderRow.bind(this);
8893
this._renderScrollComponent = this._renderScrollComponent.bind(this);
8994
this._closeButtonClicked = this._closeButtonClicked.bind(this);
95+
this._xhrIdMap = {};
9096
}
9197

9298
_enableInterception(): void {
@@ -95,49 +101,59 @@ class NetworkOverlay extends React.Component {
95101
}
96102
// Show the network request item in listView as soon as it was opened.
97103
XHRInterceptor.setOpenCallback(function(method, url, xhr) {
98-
// Add one private `_index` property to identify the xhr object,
104+
// Generate a global id for each intercepted xhr object, add this id
105+
// to the xhr object as a private `_index` property to identify it,
99106
// so that we can distinguish different xhr objects in callbacks.
100-
xhr._index = this._requests.length;
101-
const _xhr: NetworkRequestInfo = {'method': method, 'url': url};
107+
xhr._index = nextXHRId++;
108+
const xhrIndex = this._requests.length;
109+
this._xhrIdMap[xhr._index] = xhrIndex;
110+
111+
const _xhr: NetworkRequestInfo = {
112+
'method': method,
113+
'url': url
114+
};
102115
this._requests.push(_xhr);
103116
this._detailViewItems.push([]);
104-
this._genDetailViewItem(xhr._index);
117+
this._genDetailViewItem(xhrIndex);
105118
this.setState(
106119
{dataSource: this._listViewDataSource.cloneWithRows(this._requests)},
107120
this._scrollToBottom(),
108121
);
109122
}.bind(this));
110123

111124
XHRInterceptor.setRequestHeaderCallback(function(header, value, xhr) {
112-
if (xhr._index === undefined) {
125+
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
126+
if (xhrIndex === -1) {
113127
return;
114128
}
115-
const networkInfo = this._requests[xhr._index];
129+
const networkInfo = this._requests[xhrIndex];
116130
if (!networkInfo.requestHeaders) {
117131
networkInfo.requestHeaders = {};
118132
}
119133
networkInfo.requestHeaders[header] = value;
120-
this._genDetailViewItem(xhr._index);
134+
this._genDetailViewItem(xhrIndex);
121135
}.bind(this));
122136

123137
XHRInterceptor.setSendCallback(function(data, xhr) {
124-
if (xhr._index === undefined) {
138+
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
139+
if (xhrIndex === -1) {
125140
return;
126141
}
127-
this._requests[xhr._index].dataSent = data;
128-
this._genDetailViewItem(xhr._index);
142+
this._requests[xhrIndex].dataSent = data;
143+
this._genDetailViewItem(xhrIndex);
129144
}.bind(this));
130145

131146
XHRInterceptor.setHeaderReceivedCallback(
132147
function(type, size, responseHeaders, xhr) {
133-
if (xhr._index === undefined) {
148+
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
149+
if (xhrIndex === -1) {
134150
return;
135151
}
136-
const networkInfo = this._requests[xhr._index];
152+
const networkInfo = this._requests[xhrIndex];
137153
networkInfo.responseContentType = type;
138154
networkInfo.responseSize = size;
139155
networkInfo.responseHeaders = responseHeaders;
140-
this._genDetailViewItem(xhr._index);
156+
this._genDetailViewItem(xhrIndex);
141157
}.bind(this)
142158
);
143159

@@ -150,16 +166,17 @@ class NetworkOverlay extends React.Component {
150166
responseType,
151167
xhr,
152168
) {
153-
if (xhr._index === undefined) {
169+
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
170+
if (xhrIndex === -1) {
154171
return;
155172
}
156-
const networkInfo = this._requests[xhr._index];
173+
const networkInfo = this._requests[xhrIndex];
157174
networkInfo.status = status;
158175
networkInfo.timeout = timeout;
159176
networkInfo.response = response;
160177
networkInfo.responseURL = responseURL;
161178
networkInfo.responseType = responseType;
162-
this._genDetailViewItem(xhr._index);
179+
this._genDetailViewItem(xhrIndex);
163180
}.bind(this)
164181
);
165182

@@ -296,11 +313,24 @@ class NetworkOverlay extends React.Component {
296313
return JSON.stringify(value);
297314
}
298315
if (typeof value === 'string' && value.length > 500) {
299-
return String(value).substr(0, 500).concat('\n***TRUNCATED TO 500 CHARACTERS***');
316+
return String(value).substr(0, 500).concat(
317+
'\n***TRUNCATED TO 500 CHARACTERS***');
300318
}
301319
return value;
302320
}
303321

322+
_getRequestIndexByXHRID(index: number): number {
323+
if (index === undefined) {
324+
return -1;
325+
}
326+
const xhrIndex = this._xhrIdMap[index];
327+
if (xhrIndex === undefined) {
328+
return -1;
329+
} else {
330+
return xhrIndex;
331+
}
332+
}
333+
304334
/**
305335
* Generate a list of views containing network request information for
306336
* a XHR object, to be shown in the detail scrollview. This function
@@ -324,7 +354,7 @@ class NetworkOverlay extends React.Component {
324354
);
325355
}
326356
// Re-render if this network request is showing in the detail view.
327-
if (this.state.detailRowID != null && this.state.detailRowID == index) {
357+
if (this.state.detailRowID != null && Number(this.state.detailRowID) === index) {
328358
this.setState({newDetailInfo: true});
329359
}
330360
}

Libraries/Utilities/XHRInterceptor.js

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,32 @@ const XHRInterceptor = {
7777
// Override `open` method for all XHR requests to intercept the request
7878
// method and url, then pass them through the `openCallback`.
7979
XMLHttpRequest.prototype.open = function(method, url) {
80-
openCallback(method, url, this);
80+
if (openCallback) {
81+
openCallback(method, url, this);
82+
}
8183
originalXHROpen.apply(this, arguments);
8284
};
8385

8486
// Override `setRequestHeader` method for all XHR requests to intercept
8587
// the request headers, then pass them through the `requestHeaderCallback`.
8688
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
87-
requestHeaderCallback(header, value, this);
89+
if (requestHeaderCallback) {
90+
requestHeaderCallback(header, value, this);
91+
}
8892
originalXHRSetRequestHeader.apply(this, arguments);
8993
};
9094

9195
// Override `send` method of all XHR requests to intercept the data sent,
9296
// register listeners to intercept the response, and invoke the callbacks.
9397
XMLHttpRequest.prototype.send = function(data) {
94-
sendCallback(data, this);
98+
if (sendCallback) {
99+
sendCallback(data, this);
100+
}
95101
if (this.addEventListener) {
96102
this.addEventListener('readystatechange', () => {
103+
if (!isInterceptorEnabled) {
104+
return;
105+
}
97106
if (this.readyState === this.HEADERS_RECEIVED) {
98107
const contentTypeString = this.getResponseHeader('Content-Type');
99108
const contentLengthString =
@@ -105,22 +114,26 @@ const XHRInterceptor = {
105114
if (contentLengthString) {
106115
responseSize = parseInt(contentLengthString, 10);
107116
}
108-
headerReceivedCallback(
109-
responseContentType,
110-
responseSize,
111-
this.getAllResponseHeaders(),
112-
this,
113-
);
117+
if (headerReceivedCallback) {
118+
headerReceivedCallback(
119+
responseContentType,
120+
responseSize,
121+
this.getAllResponseHeaders(),
122+
this,
123+
);
124+
}
114125
}
115126
if (this.readyState === this.DONE) {
116-
responseCallback(
117-
this.status,
118-
this.timeout,
119-
this.response,
120-
this.responseURL,
121-
this.responseType,
122-
this,
123-
);
127+
if (responseCallback) {
128+
responseCallback(
129+
this.status,
130+
this.timeout,
131+
this.response,
132+
this.responseURL,
133+
this.responseType,
134+
this,
135+
);
136+
}
124137
}
125138
}, false);
126139
}

0 commit comments

Comments
 (0)