Publicly viewable at https://docs.google.com/document/d/1fMxcEFiVj-H5vmuxhPqsxJAl98GS6_yZ-wqB6j9Wmy0/pub
Adam Rice <ricea@chromium.org>
Thanks to Takeshi Yoshino <tyoshino@chromium.org> for advice and discussion
We need to be able to delete a net::WebSocketChannel object when it is no longer needed. However, we need to ensure that no accesses are made to the WebSocketChannel after it has been deleted. This is particularly difficult when it is deleted due to an exceptional condition such as a WebSocket protocol error or IPC error.
WebSockets are initiated by renderers or WebWorkers. So the logical ownership of the WebSocket is with the child process.
In practice this means that the content::WebSocketDispatcherHost is the ultimate owner of all objects related to WebSockets for a particular process. The ownership graph looks like this:
As this diagram implies, when the WebSocketDispatcherHost deletes the content::WebSocketHost, everything else related to the socket will be cleanly and synchronously deleted.
The difficulty arises for three reasons:
WebSocketHost::EventInterface handles 5 types of events:
Each of these translate 1:1 to an IPC which is sent to the renderer by WebSocketDispatcherHost. The channel must be deleted if the OnAddChannelResponse event has “fail” set to true, and when the OnDropChannel event occurs.
In addition, since any IPC can fail, it may be necessary to delete the channel in response to any of these events.
The signature of all the event methods will be changed to return a value indicating whether the channel has been deleted, provisionally WebSocketEventInterface::CHANNEL_ALIVE or WebSocketEventInterface::CHANNEL_DELETED. Each method in WebSocketChannel which issues an event must check if the return value was CHANNEL_DELETED and return without doing any further processing if it was.
In some cases, the call-stack within WebSocketChannel contains several methods. Most notably ReadFrames() → OnReadDone() → ProcessFrame() → HandleFrame() → FailChannel(). All these methods must be modified so they can return CHANNEL_DELETED to their caller, passing CHANNEL_DELETED back up the call-stack.
The impact on the lower levels of the implementation is minor. Implementations of WebSocketStream need to be aware that they may be deleted while executing the callback that was passed to ReadFrames(). In practice this constraint does not appear problematic; net::WebSocketBasicStream already fulfills the requirement.