Implement backend actor for WebSocket inspection
Categories
(DevTools :: Netmonitor, enhancement, P3)
Tracking
(firefox69 fixed)
Tracking | Status | |
---|---|---|
firefox69 | --- | fixed |
People
(Reporter: Honza, Assigned: tanhengyeow)
References
(Blocks 1 open bug)
Details
(Whiteboard: [devtools-backward-compat])
Attachments
(3 files, 6 obsolete files)
WebSocket inspection feature needs an backend actor responsible for intercepting WS communication and sending data back to the client.
Honza
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Comment 1•5 years ago
|
||
I am attaching a test patch that registers nsIWebSocketEventListener
to nsIWebSocketEventService
service.
Some notes:
-
The registration happens on the main process
-
The callbacks (frameSent/frameReceived) are handled on the main process and provides correct data (e.g. I can see the payload)
-
But, I am only seeing frameSent/frameReceived being executed. The other callbacks like: webSocketOpened/webSocketCreated/webSocketClosed are not executed for me.
@Alex, does the patch make sense?
@Andrea, I am testing the new interface implemented in bug 1542170, but some callbacks are not executed for me? (see #3) What could be wrong here?
Honza
Comment 2•5 years ago
|
||
Yes, this is normal because webSocketOpened/webSocketCreated/webSocketClosed are delivered by the WebSocket object, which runs on the content process. FrameSent/frameReceived are delivered on the parent process and sent, via IPC message, to the content process as well.
Can the actor live in the content process instead of the parent? If not we need to find a way to send those 2 messages to the parent process as well.
Reporter | ||
Comment 3•5 years ago
|
||
(In reply to Andrea Marchesini [:baku] from comment #2)
Yes, this is normal because webSocketOpened/webSocketCreated/webSocketClosed are delivered by the WebSocket object, which runs on the content process. FrameSent/frameReceived are delivered on the parent process and sent, via IPC message, to the content process as well.
Thanks Andrea, it makes sense to me now.
Can the actor live in the content process instead of the parent? If not we need to find a way to send those 2 messages to the parent process as well.
Yes, the actor can be instantiated in the content process and I updated my testing patch to do that. It works a expected now.
Honza
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Comment 4•5 years ago
|
||
The attached patch is introducing a new WebSocketActor (+ spec & front) that should be responsible for intercepting WebSocket traffic on specific page. It should run within content process.
@Julian, the actor isn't instantiated for me. I even tried to do it in ContentProcessTargetActor
, but not sure if this is ok.
What could be wrong with my patch?
Honza
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Updated•5 years ago
|
Assignee | ||
Comment 5•5 years ago
|
||
Implement backed actor for WebSocket inspection.
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Updated•5 years ago
|
Comment 6•5 years ago
|
||
Comment on attachment 9067311 [details] [diff] [review] websocket-actor1.js Review of attachment 9067311 [details] [diff] [review]: ----------------------------------------------------------------- (not a complete review, just focused on making sure we could instanciate the actor) ::: devtools/client/netmonitor/src/connector/firefox-connector.js @@ +75,5 @@ > this.tabTarget.on("navigate", this.navigate); > > // Initialize Emulation front for network throttling. > this.emulationFront = await this.tabTarget.getFront("emulation"); > + this.webSocketFront = await this.tabTarget.getFront("webSocket"); The actor will be instanciated on demand as soon as you call a method on it. Maybe you want to add a startListening/stopListening API? ::: devtools/server/actors/network-monitor/websocket-actor.js @@ +8,5 @@ > +const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); > +const { webSocketSpec } = require("devtools/shared/specs/websocket"); > + > +const webSocketEventService = Cc["@mozilla.org/websocketevent/service;1"] > + .getService(Ci.nsIWebSocketEventService); We should add `const { Cc, Ci } = require("chrome");` (this only breaks in the Browser Content Toolbox, not really sure why at the moment) ::: devtools/server/actors/utils/actor-registry.js @@ +233,5 @@ > prefix: "changes", > constructor: "ChangesActor", > type: { target: true }, > }); > + this.registerModule("devtools/server/actor/network-monitor/websocket-actor", { should be actors/ instead of actor/ here
Updated•5 years ago
|
Reporter | ||
Comment 7•5 years ago
|
||
@Julian, thanks for the review, works like a charm now!
@HengYeow: please look at the patch now and try it out
STR (just for testing purposes):
- Load http://janodvarko.cz/test/websockets/
- Reload to see at least one request in the Network panel
- Click any request to open the Headers side panel.
getFrames
is called on the actor now, check out the Browser console (but, no frames at this point) - Create WS connection by clicking on
Connect
and send a few frames - Close & open the side panel to execute the
getFrames
again, check out the browser console, you should see the frames.
Honza
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Comment 8•5 years ago
|
||
Summary of actor's API that should be implemented in this report:
Methods
- getFrames(httpChannelId) - returns list of frames for specific channel (with cropped payload, we probably want to use LongStringActor [1])
- startListening() - adds nsIWebSocketEventService listeners
- stopListening() - removes nsIWebSocketEventService listener
- getFramePayload(frameId) - returns full payload for specific frame
Events
- frameSent - sends frame data with a flag 'sent': { type: 'sent', data: frame }
- frameReceived - sends frame data with a flag 'received': { type: 'received, data: frame }
[1] Some useful links:
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/client/netmonitor/src/connector/firefox-connector.js#359
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/server/actors/network-event.js#316
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/server/actors/string.js#12
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/client/netmonitor/src/connector/firefox-data-provider.js#178
Support for connectionOpened/Closed and isConnectionAlive can be done in different report
Anything else?
Honza
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Comment 9•5 years ago
|
||
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #8)
Anything else?
Could you also take a look at CDP to get some inspiration about what API are there?
Honza
Assignee | ||
Comment 10•5 years ago
|
||
Implement backend actor for WebSocket inspection
Updated•5 years ago
|
Updated•5 years ago
|
Assignee | ||
Updated•5 years ago
|
Assignee | ||
Comment 11•5 years ago
|
||
Hi Honza!
Attached a preliminary Phabricator patch for this bug. Also, I looked through CDP's protocol and below is the summary of findings (method implementations are based on CDP).
Similar APIs:
- webSocketClosed(requestId, timestamp)
- webSocketCreated(requestId, url, initiator)
- webSocketFrameReceived(requestId, timestamp, response)
- webSocketFrameSent(requestId, timestamp, response)
Additional APIs from CDP:
- webSocketFrameError(requestId, timestamp, errorMessage)
- webSocketHandshakeResponseReceived(requestId, timestamp, response)
- webSocketWillSendHandshakeRequest(requestId, timestamp, wallTime, request)
Reporter | ||
Comment 12•5 years ago
|
||
Great, thanks. This looks good so far!
Honza
Assignee | ||
Comment 13•5 years ago
|
||
Hi Honza
I updated the Phabricator patch and a working MVP can be observed now!
Some notes for STR:
- WebSocketActor starts listening on
FirefoxConnector.addListeners
now, not whenHeadersPanel
is opened. - I'm toggling between these two test sites test 1, test 2 for testing, both of them are intermittent.
- When a frame is sent/received, console logs can be seen from both
WebSocketActor
andWebSocketFront
(events emitted fromWebSocketActor
received and printed out) - Opening the
HeadersPanel
of the respective WebSocket request from the Network Panel would call 2 functions (for testing purposes):getFrames(httpChannelId)
- Returns an array of frames whose payload is wrapped byLongStringActor
getFramePayload(frameId)
- Returns the full payload of the specified frame. (Always returns the first frame for now)
I'm facing an exception when the WebSocketActor
is destroyed (when I closed the whole DevTools). I've attached an image to show the error.
As discussed in one of the meetings, we would want to store the frames/relevant data in the store and information in the WebSocketFront
should communicate with FirefoxConnector
and FirefoxDataProvider
and then eventually reach the store. Should we do this in the same bug?
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #8)
Summary of actor's API that should be implemented in this report:
Methods
- getFrames(httpChannelId) - returns list of frames for specific channel (with cropped payload, we probably want to use LongStringActor [1])
- startListening() - adds nsIWebSocketEventService listeners
- stopListening() - removes nsIWebSocketEventService listener
- getFramePayload(frameId) - returns full payload for specific frame
Events
- frameSent - sends frame data with a flag 'sent': { type: 'sent', data: frame }
- frameReceived - sends frame data with a flag 'received': { type: 'received, data: frame }
[1] Some useful links:
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/client/netmonitor/src/connector/firefox-connector.js#359
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/server/actors/network-event.js#316
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/server/actors/string.js#12
- https://searchfox.org/mozilla-central/rev/ddb81c7a43ffada1f6cb4200c4f625e50e44dcf3/devtools/client/netmonitor/src/connector/firefox-data-provider.js#178
Support for connectionOpened/Closed and isConnectionAlive can be done in different report
Anything else?
Honza
Assignee | ||
Comment 14•5 years ago
|
||
Reporter | ||
Comment 15•5 years ago
|
||
Thanks for the patch!
(In reply to Heng Yeow (:tanhengyeow) from comment #13)
I'm facing an exception when the
WebSocketActor
is destroyed (when I closed the whole DevTools). I've attached an image to show the error.
It's because stopListening
is called twice and you are trying to remove the WS listener twice. The second try fails with the above exception
- It's called from
WebSocketActor.destroy
- It's called through
WebSocketFront.stopListening
fromFirefoxConnector.removeListeners
I think you can introduce a helper flag indicating that the listener has been removed. The same flag can be used to ensure that the actor doesn't add the listener twice. The name could be e.g. listening
(set to true in startListening and to false in stopListening)
As discussed in one of the meetings, we would want to store the frames/relevant data in the store and information in the
WebSocketFront
should communicate withFirefoxConnector
andFirefoxDataProvider
and then eventually reach the store. Should we do this in the same bug?
Yep, feel free to do it in the same bug & in the same patch.
Honza
Reporter | ||
Comment 16•5 years ago
|
||
Attaching a patch that fixes WS listener after page reload. We need to re-register the listeners since the inner window changes
Honza
Assignee | ||
Comment 17•5 years ago
|
||
Hi Honza!
Updated the Phab patch! To observe a payload that is wrapped in LongStringActor
, the payload must be longer than the defined constant, which is 10000
.
You should see something like the attached image if the payload exceeds 10000
.
Assignee | ||
Comment 18•5 years ago
|
||
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Comment 19•5 years ago
|
||
(In reply to Heng Yeow (:tanhengyeow) from comment #17)
Hi Honza!
Updated the Phab patch! To observe a payload that is wrapped in
LongStringActor
, the payload must be longer than the defined constant, which is10000
.You should see something like the attached image if the payload exceeds
10000
.
Great work here, thanks!
I commented in Phab, the patch needs rebase on top of the latest m-c
Honza
Assignee | ||
Comment 20•5 years ago
|
||
Latest patch ready for review :) Try results green.
Comment 21•5 years ago
|
||
Pushed by jodvarko@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/de1dc8a5ce54 Implement backend actor for WebSocket inspection. r=Honza
Comment 22•5 years ago
|
||
Backed out changeset de1dc8a5ce54 (bug 1552458) for xpcshell failures on service-worker-registration
Failure log: https://treeherder.mozilla.org/logviewer.html#/jobs?job_id=251116407&repo=autoland&lineNumber=2441
Backout: https://hg.mozilla.org/integration/autoland/rev/ceec9dc6f66dc153772d8a8c99548ece16a49a93
Reporter | ||
Comment 23•5 years ago
|
||
(In reply to Pulsebot from comment #21)
Pushed by jodvarko@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/de1dc8a5ce54
Implement backend actor for WebSocket inspection. r=Honza
Thanks for quick fix.
Comments in Phab
Honza
Comment 24•5 years ago
|
||
Pushed by jodvarko@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/ee33e076cbcb Implement backend actor for WebSocket inspection. r=Honza,jdescottes
Comment 25•5 years ago
|
||
bugherder |
Updated•5 years ago
|
Assignee | ||
Comment 26•5 years ago
|
||
Summary of the final patch:
-
WebSocketActor
lives in the content process and implements callback methods based on this interface file. -
onNavigate
andonWillNavigate
implemented inWebSocketActor
to facilitate calling thestartListening
/stopListening
functions used to intercept WebSocket traffic. Required because theinnerWindowID
changes based on the current view e.g. on first reload of the Network Panel. This is important as the functionwebSocketEventService.addListener(innerWindowID, this);
depends on theinnerWindowID
. If the value changes, we need to registerwebSocketEventService
with a new listener. -
Spec file and
WebSocketFront
setup to intercept events emitted by the backendWebSocketActor
.WebSocketFront
is able to call methods APIs implemented inWebSocketActor
as long as the methods are defined in the spec file. WhenFirefoxConnector
is connected to/disconnected from the backend,this.webSocketFront.startListening()
/this.webSocketFront.stopListening()
gets called respectively. Complements the previous point with a boolean flag to check ifwebSocketEventService
has an existing listener. A new listener would only be registered if there are no existing listeners. -
Implementation is hidden under the pref setting
devtools.netmonitor.features.webSockets
. -
WebSocketFront
transports events/data emitted byWebSocketActor
to theFirefoxConnector
(connects to the Firefox backend), and to theFirefoxDataProvider
(acts as a single point to keep track of all RDP requests). Frame payload is wrapped in aLongStringActor
object, to be resolved in the frontend using thegetLongString
method. -
With reference to comment 8,
getFrames(httpChannelId)
andgetFramePayload(frameId)
would not be implemented as discussed. This is because the events/data emitted byWebSocketActor
would be added to the Redux Store from theFirefoxDataProvider
. Work would be done in Bug 1555625.
Reporter | ||
Updated•5 years ago
|
Description
•