Chromium WebSocket design doc

Contact: tyoshino, yhirano, ricea

Original authors: yuzo, ukai, tyoshino

Status: Draft (as of Oct 23, 2009)

Table of Contents

  1. Chromium WebSocket design doc
  1. Objective
  2. High Level Structure
  3. WebKit API
  4. Flows
  1. Establish WebSocket connection
  2. Send a message
  3. Receive a message
  4. Disconnect the connection (from JavaScript)
  5. Connection closed (from raw connection)
  1. Other modification
  2. Security Considerations
  3. Layout Test Plan
  4. Open Issues
  5. Work Estimates
  6. Reference
  7. Document History

Objective

 We'll implement client side of WebSocket in Chromium.

This document describes Chromium HTML5 WebSocket design.

It is based on WebKit WebSocket design and describes chromium side in more detail.

High Level Structure

Legend:

V8WebSocketCustom:  custom binding of WebSocket for V8 Engine.  It sits between V8 and WebCore::WebSocket.

will be in WebKit/WebCore/binding/v8/custom/.   

WebCore::WebSocket: C++ class corresponding to WebSocket interface in JavaScript.  It also implements WebCore::WebSocketChannelClient so that it would receive events from WebCore::WebSocketChannelHandle. 

will be in WebKit/WebCore/websockets/.

WebCore::WebSocketChannel: WebSocket channel in WebCore.  It process WebSocket handshaking and data framing. Use WebCore::WebSocketHandshake to handshake. packing data in a frame and sent it to WebCore::SocketStreamHandle.  It implements WebCore::SocketStreamHandleClient, so that it will receive a frame from WebCore::SocketStreamHandle and depacking data from a frame.

WebCore::WebSocketHandshake: Perform WebSocket handshaking.

WebCore::SocketStreamHandle: Socket stream handle in WebCore/platform/network.  This is boundary between WebCore and platform.

It handles raw connection establishment (direct / via proxy / TLS) and send/receive data on the connection. 

In chromium, it is implemented by WebKit::WebSocketStreamHandle.  Each instance of WebKit::WebSocketStreamHandle has a unique socket_id that is used in chromium IPC. socket_id is given by browser process in response of ViewHostMsg_SocketStream_Connect.

In WebKit::WebSocketStreamHandle, it uses webkit_glue::WebSocketStreamHandleBridge to communicate with browser process. it will send ViewHostMsg_SocketStream* messages to browser or receive ViewMsg_SocketStream* messages from browser.

will be in webkit/api/src and webkit/glue.  Chromium IPC code is in chrome/browser/renderer_host.

All messages are "control" and async (IPC_MESSAGE_CONTROL), except Connect (this is sync messages).  Each socket is identified by socket_id, which is given by SocketStreamHost in browser process in response of ViewHostMsg_SocketStream_Connect.

WebKit::WebSocketStreamHandle: Explained above.

in webkit/api/public/WebSocketStreamHandle.h

webkit_glue::WebSocketStreamHandleImpl: Explained above. Implementation of WebKit::WebSocketStreamHandle

in webkit/glue/websocketstreamhandle_impl.*

webkit_glue::WebSocketStreamHandleBridge: Bridge interface explained above.

in webkit/glue/websocketstreamhandle_bridge.h

IPCWebSocketStreamHandleBridge (internal class): Implementaion of webkit_glue::WebSocketStreamHandleBridge

in chrome/renderer/socket_stream_dispatcher.cc

SocketStreamDispatcher: Handling IPC. Peer of SocketStreamDispatcherHost.

in chrome/renderer/socket_stream_dispatcher.cc

ViewHostMsg_SocketStream*: IPC messages sent from renderer to browser.

ViewMsg_SocketStream*: IPC messages sent from browser to renderer.

SocketStreamDispatcherHost: Processes ViewHostMsg_SocketStream* and sends ViewMsg_SocketStream*.  Injected in ResourceMessageFilter.

in chrome/browser/renderer_host/socket_stream_dispatcher_host.*

SocketStreamHost: Host of WebCore::WebSocketStreamHandle. It will receive ViewHostMsg_SocketStream*. messages from renderer.  

in chrome/browser/renderer_host/socket_stream_host.*

It manages mapping between socket_id and SocketStream.

SocketStream: Manages socket stream.  connect/read/write/close over ClientSocket (net/socket).

in net/socket_stream/socket_stream.*

WebKit API

We'll add new API in WebKit API / WebKitClient API. 

webkit/api/public/WebKit.h:

namespace WebKit {

...

  // Enables HTML5 WebSocket support.

  WEBKIT_API void enableWebSockets();

...

}

webkit/api/public/WebKitClient.h:

namespace WebKit {

...

  // Network

...

  // Returns a new WebKit::WebSocketStreamHandle instance.

  virtual WebSocketStreamHandle* createSocketStreamHandle() = 0;

...

}

webkit/api/public/WebSocketStreamHandle.h:

namespace WebKit {

  class WebSocketStreamHandleClient;

  class WebData;

  class WebSocketStreamHandle {

  public:

      virtual ~WebSocketStreamHandle() {}

      virtual void connect(const WebURL&, WebSocketStreamHandleClient*) = 0;

      virtual void send(const WebData& data) = 0;

      virtual void close() = 0;

  };

}

webkit/api/public/WebSocketStreamHandleClient.h:

namespace WebKit {

  class WebSocketStreamHandle;

  class WebData;

  class WebSocketStreamHandleClient {

  public:

     virtual void didOpen(WebSocketStreamHandle*, int max_amount_send_allowed) = 0;

         virtual void canDataSent(WebSocketStraemHandle*, int amount_sent) = 0;

     virtual void didReceiveData(WebSocketStreamHandle*, const WebData&) = 0;

     virtual void didClose(WebSocketStreamHandle*) = 0;

  };

}

Flows

Establish WebSocket connection

In renderer:

  1. in JavaScript, WebSocket(url [,protocol]) constructor is called
  2. create WebCore::WebSocket object and associate it with the JavaScript WebSocket object.
  3. it calls WebCore::WebSocketChannel::connect(), which calls WebCore::SocketStreamHandle::connect().
  4. WebCore::SocketStreamHandle::connect() calls WebKit::WebSocketStreamHandle::connect()
  5. in WebKit::WebSocketStreamHandle::connect(), send ViewHostMsg_SocketStream_Connect message to browser and get socket_id.

In browser: (IO thread)

  1. handle ViewHostMsg_SocketStream_Connect message by SocketStreamDispatcherHost.
  2. create SocketStreamHost for the request. Assign new socket_id to it. SocketStreamDispatcherHost is its delegate.
  3. create SocketStreamRequest for it.
  4. SocketStreamRequest start job.
  5. return socket_id to the renderer.
  6. In SocketStreamJob.
  1. connect via proxy with method="CONNECT" over TCPClientSocket if proxy is configured.
  2. use SSLClientSocket if the connection is secure.
  1. Once connected, send ViewMsg_SocketStream_Connected to the renderer.

In renderer

  1. handle ViewMsg_SocketStream_Connected message.  find associated WebSocketStreamHandle for its socket_id.
  2. make socket idle.
  3. call WebSocketClient::didOpen.  WebSocketChannel start performing WebSocket handshaking.
  1. send client handshake message
  2. receive server handshake message
  3. check handshake message.
  1. if it's ok, establish the connection, call m_client->didConnect()
  2. readyState is OPEN and "open" event is dispatched in JavaScript

Send a message

In renderer:

  1. in JavaScript, WebSocket::send is used to send message.
  2. WebCore::WebSocket::send() calls m_channel->send().
  3. WebCore::WebSocketChannel::send() builds WebSocket frame and call m_handle->send(buf, buflen)
  4. WebCore::SocketStreamHandle::send() creates WebData from buf,buflen and call WebKit::WebSocketStreamHandle::send().
  5. In WebKit::WebSocketStreamHandle::send()
  1. if it will exceed max_amount_send_allowed, return false.
  1. Otherwise, send ViewHostMsg_SocketStream_SendData message to browser. 

In browser: (IO thread)

  1. handle ViewHostMsg_SocketStream_SendData message by SocketStreamDispatcherHost.
  2. find SocketStreamHost by socket_id.
  3. call SocketStreamJob::Send for the SocketStreamHost.
  4. SocketStreamHost tries to write on ClientSocket.
  5. ClientSocket wrote data, call canSend of SocketStreamRequest::Delegate.
  6. SocketStreamDispatcherHost send ViewMsg_SocketStream_DataSent to renderer

In renderer:

  1. handle ViewMsg_SocketStream_CanSend in SocketStreamDispatcher. find associated WebSocketStreamHandle for its socket_id.
  2. make socket idle.
  3. call WebSocketStreamHandleClient::DataSent

Receive a message

In browser: (IO thread)

  1. ClientSocket receives data from the connection (TCPClientSocket or SSLCLientSocket)
  2. call didReceiveData of SocketStreamRequest::Delegate.
  3. SocketStreamDispacherHost send ViewMsg_SocketStream_ReceivedData to renderer

In renderer:

  1. handle ViewMsg_SocketStream_ReceivedData message.  find associated WebSocketStreamHandle for its socket_id.
  2. call client()->didReceiveData()
  3. WebCore::WebSocketChannel::didReceive() process WebSocket frame in m_handle->bufferedData()
  1. remove WebSocket frame from m_handle->bufferedData()
  2. extract message if frame_type == 0x00, call m_client->didReceiveMessage()
  1. WebCore::WebSocket::didReceiveMessage() create MessageEvent for the message and dispatch the event.

Disconnect the connection (from JavaScript)

In renderer:

  1. In JavaScript, WebSocket::close is called
  2. WebCore::WebSocket::close() change readyState to CLOSED and calls m_channel->close()
  3. WebCore::WebSocketChannel::disconnect() calls m_handle->close()
  4. WebCore::SocketStreamHandle::close(), calls WebKit::WebSocketStreamHandle::close()
  5. WebKit::WebSocketStreamHandle::close() sends ViewHostMsg_SocketStream_Close message to browser

In browser: (IO thread)

  1. handle ViewHostMsg_SocketStream_Close message by SocketStreamDispatcherHost.  find associated SocketStreamHost for its socket_id.
  2. call SocketStreamJob::Close for the SocketStreamHost.
  3. SocketStreamHost closes the ClientSocket.
  4. SocketStreamJob will notice the connection is closed.

Connection closed (from raw connection)

In browser: (IO thread)

  1. SocketStreamJob notices client socket is not connected.  delete the client socket and notify it delegate.
  2. SocketStreamDispatcherHost sends ViewMsg_SocketStream_Closed to renderer and delete SocketStreamHost for the socket_id.

In renderer:

  1. handle ViewMsg_SocketStream_Closed message.  find associate WebSocketStreamHandle for its socket_id.
  2. WebSocketStreamHandle calls client()->didClose()
  3. WebCore::WebSocketChannel::didClose() destroys m_handle and call m_client->didClose()
  4. WebCore::WebSocket::didClose() changes readyState to CLOSED, create "close" event and dispatch the event.

 Other modification

Adds "ws" and "wss" in googleurl. (WebSocket URL scheme)

Security Considerations

See Security Consideration secion in WebKit WebSocket design doc.

** Any chromium specific ISSUE? ** ...

Layout Test Plan

WebKit and Chromium uses different http server for layout tests.  WebKit uses apache  (launched by WebKitTools/Scripts/run-webkit-httpd, configuration in LayoutTests/http/conf/).  Chromium uses lighttpd (launched by webkit/tools/layout_tests/run_webkit_tests.py that imports webkit/tools/layout_tests/layout_package/http_server.py).

Since there are no apache WebSocket module nor lighttpd WebSockets module yet.

So, we're developing simple WebSocket server implementation by python to use it for layout tests in both WebKit and chromium.

Open Issues

Work Estimates

By End of '09 Q2: design doc reviewed.

By End of '09 Q3: land minimum functionalities in webkit.

By End of Oct '09: land basic functionalities in chromium/webkit.  ready to use in trunk.

Reference

Document History

Date

Author

Description

Oct 23, 2009

tyoshino

Updated High Level Architecture section and the diagram in it

Sept 8, 2009

ukai

CanSend -> DataSent. add max_amount_send_allowed and amount_sent

Sept 4, 2009

ukai

make SendData async (remove accepted)

Sept 2, 2009

ukai

Change to SocketStreamHandle.

June 26, 2009

ukai

updating by chromium-dev review feedback

June 23, 2009

ukai

Initial draft