gecko-dev/devtools/server/actors/network-event.js
Alexandre Poirot 621a50208d Bug 1026583 - Avoid sending network monitor events when the related actors are already destroyed. r=Honza
Summary: Depends On D4329

Reviewers: Honza!

Tags: #secure-revision

Bug #: 1026583

Differential Revision: https://phabricator.services.mozilla.com/D4330

MozReview-Commit-ID: H8j43mSIUnc
2018-08-28 05:42:33 -07:00

540 lines
14 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const protocol = require("devtools/shared/protocol");
const { networkEventSpec } = require("devtools/shared/specs/network-event");
const { LongStringActor } = require("devtools/server/actors/string");
/**
* Creates an actor for a network event.
*
* @constructor
* @param object netMonitorActor
* The parent NetworkMonitorActor instance for this object.
*/
const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
initialize(netMonitorActor) {
// Necessary to get the events to work
protocol.Actor.prototype.initialize.call(this, netMonitorActor.conn);
this.netMonitorActor = netMonitorActor;
this.conn = this.netMonitorActor.conn;
this._request = {
method: null,
url: null,
httpVersion: null,
headers: [],
cookies: [],
headersSize: null,
postData: {},
};
this._response = {
headers: [],
cookies: [],
content: {},
};
this._timings = {};
this._stackTrace = {};
this._discardRequestBody = false;
this._discardResponseBody = false;
},
_request: null,
_response: null,
_timings: null,
/**
* Returns a grip for this actor for returning in a protocol message.
*/
form() {
return {
actor: this.actorID,
startedDateTime: this._startedDateTime,
timeStamp: Date.parse(this._startedDateTime),
url: this._request.url,
method: this._request.method,
isXHR: this._isXHR,
cause: this._cause,
fromCache: this._fromCache,
fromServiceWorker: this._fromServiceWorker,
private: this._private,
isTrackingResource: this._isTrackingResource,
};
},
/**
* Releases this actor from the pool.
*/
destroy(conn) {
if (!this.netMonitorActor) {
return;
}
if (this._request.url) {
this.netMonitorActor._networkEventActorsByURL.delete(this._request.url);
}
if (this.channel) {
this.netMonitorActor._netEvents.delete(this.channel);
}
this.netMonitorActor = null;
protocol.Actor.prototype.destroy.call(this, conn);
},
release() {
// Per spec, destroy is automatically going to be called after this request
},
/**
* Set the properties of this actor based on it's corresponding
* network event.
*
* @param object networkEvent
* The network event associated with this actor.
*/
init(networkEvent) {
this._startedDateTime = networkEvent.startedDateTime;
this._isXHR = networkEvent.isXHR;
this._cause = networkEvent.cause;
this._fromCache = networkEvent.fromCache;
this._fromServiceWorker = networkEvent.fromServiceWorker;
this._isTrackingResource = networkEvent.isTrackingResource;
this._channelId = networkEvent.channelId;
// Stack trace info isn't sent automatically. The client
// needs to request it explicitly using getStackTrace
// packet. NetmonitorActor may pass just a boolean instead of the stack
// when the actor is in parent process and stack is in the content process.
this._stackTrace = networkEvent.cause.stacktrace;
delete networkEvent.cause.stacktrace;
networkEvent.cause.stacktraceAvailable =
!!(this._stackTrace &&
(typeof this._stackTrace == "boolean" || this._stackTrace.length));
for (const prop of ["method", "url", "httpVersion", "headersSize"]) {
this._request[prop] = networkEvent[prop];
}
// Consider as not discarded if networkEvent.discard*Body is undefined
this._discardRequestBody = !!networkEvent.discardRequestBody;
this._discardResponseBody = !!networkEvent.discardResponseBody;
this._truncated = false;
this._private = networkEvent.private;
},
/**
* The "getRequestHeaders" packet type handler.
*
* @return object
* The response packet - network request headers.
*/
getRequestHeaders() {
return {
headers: this._request.headers,
headersSize: this._request.headersSize,
rawHeaders: this._request.rawHeaders,
};
},
/**
* The "getRequestCookies" packet type handler.
*
* @return object
* The response packet - network request cookies.
*/
getRequestCookies() {
return {
cookies: this._request.cookies,
};
},
/**
* The "getRequestPostData" packet type handler.
*
* @return object
* The response packet - network POST data.
*/
getRequestPostData() {
return {
postData: this._request.postData,
postDataDiscarded: this._discardRequestBody,
};
},
/**
* The "getSecurityInfo" packet type handler.
*
* @return object
* The response packet - connection security information.
*/
getSecurityInfo() {
return {
securityInfo: this._securityInfo,
};
},
/**
* The "getResponseHeaders" packet type handler.
*
* @return object
* The response packet - network response headers.
*/
getResponseHeaders() {
return {
headers: this._response.headers,
headersSize: this._response.headersSize,
rawHeaders: this._response.rawHeaders,
};
},
/**
* The "getResponseCache" packet type handler.
*
* @return object
* The cache packet - network cache information.
*/
getResponseCache: function() {
return {
cache: this._response.responseCache,
};
},
/**
* The "getResponseCookies" packet type handler.
*
* @return object
* The response packet - network response cookies.
*/
getResponseCookies() {
return {
cookies: this._response.cookies,
};
},
/**
* The "getResponseContent" packet type handler.
*
* @return object
* The response packet - network response content.
*/
getResponseContent() {
return {
content: this._response.content,
contentDiscarded: this._discardResponseBody,
};
},
/**
* The "getEventTimings" packet type handler.
*
* @return object
* The response packet - network event timings.
*/
getEventTimings() {
return {
timings: this._timings,
totalTime: this._totalTime,
offsets: this._offsets
};
},
/**
* The "getStackTrace" packet type handler.
*
* @return object
* The response packet - stack trace.
*/
async getStackTrace() {
let stacktrace = this._stackTrace;
// If _stackTrace was "true", it means we are in parent process
// and the stack is available from the content process.
// Fetch it lazily from here via the message manager.
if (stacktrace && typeof stacktrace == "boolean") {
const messageManager = this.netMonitorActor.messageManager;
stacktrace = await new Promise(resolve => {
const onMessage = ({ data }) => {
const { channelId, stack } = data;
if (channelId == this._channelId) {
messageManager.removeMessageListener("debug:request-stack", onMessage);
resolve(stack);
}
};
messageManager.addMessageListener("debug:request-stack", onMessage);
messageManager.sendAsyncMessage("debug:request-stack", this._channelId);
});
this._stackTrace = stacktrace;
}
return {
stacktrace,
};
},
/** ****************************************************************
* Listeners for new network event data coming from NetworkMonitor.
******************************************************************/
/**
* Add network request headers.
*
* @param array headers
* The request headers array.
* @param string rawHeaders
* The raw headers source.
*/
addRequestHeaders(headers, rawHeaders) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._request.headers = headers;
this._prepareHeaders(headers);
if (rawHeaders) {
rawHeaders = new LongStringActor(this.conn, rawHeaders);
// bug 1462561 - Use "json" type and manually manage/marshall actors to woraround
// protocol.js performance issue
this.manage(rawHeaders);
rawHeaders = rawHeaders.form();
}
this._request.rawHeaders = rawHeaders;
this.emit("network-event-update:headers", "requestHeaders", {
headers: headers.length,
headersSize: this._request.headersSize,
});
},
/**
* Add network request cookies.
*
* @param array cookies
* The request cookies array.
*/
addRequestCookies(cookies) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._request.cookies = cookies;
this._prepareHeaders(cookies);
this.emit("network-event-update:cookies", "requestCookies", {
cookies: cookies.length,
});
},
/**
* Add network request POST data.
*
* @param object postData
* The request POST data.
*/
addRequestPostData(postData) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._request.postData = postData;
postData.text = new LongStringActor(this.conn, postData.text);
// bug 1462561 - Use "json" type and manually manage/marshall actors to woraround
// protocol.js performance issue
this.manage(postData.text);
const dataSize = postData.text.str.length;
postData.text = postData.text.form();
this.emit("network-event-update:post-data", "requestPostData", {
dataSize,
discardRequestBody: this._discardRequestBody,
});
},
/**
* Add the initial network response information.
*
* @param object info
* The response information.
* @param string rawHeaders
* The raw headers source.
*/
addResponseStart(info, rawHeaders) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
rawHeaders = new LongStringActor(this.conn, rawHeaders);
// bug 1462561 - Use "json" type and manually manage/marshall actors to woraround
// protocol.js performance issue
this.manage(rawHeaders);
this._response.rawHeaders = rawHeaders.form();
this._response.httpVersion = info.httpVersion;
this._response.status = info.status;
this._response.statusText = info.statusText;
this._response.headersSize = info.headersSize;
// Consider as not discarded if info.discardResponseBody is undefined
this._discardResponseBody = !!info.discardResponseBody;
this.emit("network-event-update:response-start", "responseStart", {
response: info
});
},
/**
* Add connection security information.
*
* @param object info
* The object containing security information.
*/
addSecurityInfo(info) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._securityInfo = info;
this.emit("network-event-update:security-info", "securityInfo", {
state: info.state,
});
},
/**
* Add network response headers.
*
* @param array headers
* The response headers array.
*/
addResponseHeaders(headers) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._response.headers = headers;
this._prepareHeaders(headers);
this.emit("network-event-update:headers", "responseHeaders", {
headers: headers.length,
headersSize: this._response.headersSize,
});
},
/**
* Add network response cookies.
*
* @param array cookies
* The response cookies array.
*/
addResponseCookies(cookies) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._response.cookies = cookies;
this._prepareHeaders(cookies);
this.emit("network-event-update:cookies", "responseCookies", {
cookies: cookies.length,
});
},
/**
* Add network response content.
*
* @param object content
* The response content.
* @param object
* - boolean discardedResponseBody
* Tells if the response content was recorded or not.
* - boolean truncated
* Tells if the some of the response content is missing.
*/
addResponseContent(content, {discardResponseBody, truncated}) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._truncated = truncated;
this._response.content = content;
content.text = new LongStringActor(this.conn, content.text);
// bug 1462561 - Use "json" type and manually manage/marshall actors to woraround
// protocol.js performance issue
this.manage(content.text);
content.text = content.text.form();
this.emit("network-event-update:response-content", "responseContent", {
mimeType: content.mimeType,
contentSize: content.size,
encoding: content.encoding,
transferredSize: content.transferredSize,
discardResponseBody,
});
},
addResponseCache: function(content) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._response.responseCache = content.responseCache;
this.emit("network-event-update:response-cache", "responseCache");
},
/**
* Add network event timing information.
*
* @param number total
* The total time of the network event.
* @param object timings
* Timing details about the network event.
*/
addEventTimings(total, timings, offsets) {
// Ignore calls when this actor is already destroyed
if (!this.actorID) {
return;
}
this._totalTime = total;
this._timings = timings;
this._offsets = offsets;
this.emit("network-event-update:event-timings", "eventTimings", {
totalTime: total
});
},
/**
* Prepare the headers array to be sent to the client by using the
* LongStringActor for the header values, when needed.
*
* @private
* @param array aHeaders
*/
_prepareHeaders(headers) {
for (const header of headers) {
header.value = new LongStringActor(this.conn, header.value);
// bug 1462561 - Use "json" type and manually manage/marshall actors to woraround
// protocol.js performance issue
this.manage(header.value);
header.value = header.value.form();
}
},
});
exports.NetworkEventActor = NetworkEventActor;