Chrome 遠程調試協議分析與實戰

背景

某一天,A 君想獲取 Chrome 頁面中的性能數據,諸如時間、白屏和首屏等,因爲需要和競品進行對比分析,無法注入代碼,該怎麼辦?

此時,你也許能想到開發者工具(DevTools),也許知道Timeline(包含瀏覽器完整的行爲數據),該怎麼自動獲取到 Timeline 數據呢?

開發者工具

開發者工具(DevTools)是一個獨立的 Web 應用程序(HTML+CSS+Javascript),被集成在瀏覽器中,通過遠程調試協議(remote debugging protocol)和瀏覽器內核進行交互,直接使用 Ctrl+Shift+I 呼出。

devtools

可以在當前的瀏覽器頁面直接打開 DevTools 調試,也可以在瀏覽器之外進行調試,本文的實戰內容基於 PC 平臺瀏覽器之外的遠程調試。

遠程調試協議

遠程調試協議基於 WebSocket,利用 WebSocket 建立連接 DevTools 和瀏覽器內核的快速數據通道。DevTools 中的源代碼(Main.js:220)如下:

var ws;if ("ws" in WebInspector.queryParamsObject)
    ws = "ws://" + WebInspector.queryParamsObject.ws;else if ("page" in WebInspector.queryParamsObject) {
    var page = WebInspector.queryParamsObject.page;
    var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
    ws = "ws://" + host + "/devtools/page/" + page;}

該協議把操作劃分爲不同的域(domain),比如 DOM、Debugger、Network、Console 和 Timeline 等,可以理解爲 DevTools 中的不同功能模塊。

每個域(domain)定義了它所支持的 command 和它所產生的 event。

每個 command 包含 request 和 response 兩部分,request 部分指定所要進行的操作以及操作說要的參數,response 部分表明操作狀態,成功或失敗。

command 和 event 中可能涉及到非基本數據類型,在 domain 中被歸爲 Type,比如:'frameId': <FrameId>,其中 FrameId 爲非基本數據類型

至此,不難理解:

domain = command + event + type

遠程調試協議應用場景

  • 針對移動端的遠程調試,因爲移動平臺一般都不會提供足夠大的區域來顯示 DevTools,必須要在手機瀏覽器之外進行遠程調試,具體配置請參看這篇文章

  • 獲取 JS 的Runtime數據,常用的如window.performance和 window.chrome.loadTimes() 等

  • 獲取NetworkTimeline數據,進行自動性能分析

  • 與強大的phantomjs合體,phantomjs 暫時只支持基於 remote debugging protocol 的調試,希望能支持 Network 及 Timeline 數據的獲取,phantomjs 的最新技術請點擊進入

遠程調試協議結構

以 Page domain 爲例

command 結構如下:

Page.navigaterequest: {
    "id": <number>,
    "method": "Page.navigate",
    "params": {
        "url": <string>
    }}response: {
    "id": <number>,
    "error": <object>}

執行 Page.navigate 操作,需要參數 url,id 可以隨意指定,不過要確認全局的唯一性,因爲需要通過 id 關聯 request 和 response。

event 結構如下:

Page.loadEventFired{
    "method": "Page.loadEventFired",
    "params": {
    "timestamp": <number>
    }}

Page domain 派發 loadEventFired 事件結構數據(通過 WebSocket 的 onmessage 獲取),幷包含參數 timestamp

type 結構如下:

Frame: object
    id ( string )
        Frame unique identifier.
    loaderId ( Network.LoaderId )
        Identifier of the loader associated with this frame.
    mimeType ( string )
        Frame document's mimeType as determined by the browser.
    name ( optional string )
        Frame's name as specified in the tag.
    parentId ( optional string )
        Parent frame identifier.
    securityOrigin ( string )
        Frame document's security origin.
    url ( string )
        Frame document's URL.

Frame type 爲包含 id,loaderId,mimeType,name,parentId,securityOrigin 和 url 字段的 Object 數據類型,其中 loaderId 爲另外一個定義在 Network domain 中的 type

更多協議內容請猛戳這裏

遠程調試協議實戰

此協議用於 server 端和 client 端的通訊,所以需要先建立 server 端,然後 client 端通過協議連接到 server 端

開啓 server 服務

打開瀏覽器的遠程調試支持,並指定端口號:

./chrome --remote-debugging-port=9222

./chrome 爲已安裝的 Chrome 可執行程序

獲取 server 地址

在瀏覽器中直接輸入:

http://localhost:9222/json

獲取所有的 tabs 信息,數據格式如下:

[
    {},
    {},
    {}
]

每個 {} 的內容如下:

{
    description: "",
    devtoolsFrontendUrl: "/devtools/devtools.html?ws=localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F",
    faviconUrl: "http://www.baidu.com/favicon.ico",
    id: "A12A4B08-E5AF-4A84-A86A-A1C86E731D7F",
    thumbnailUrl: "/thumb/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F",
    title: "百度一下,你就知道",
    type: "page",
    url: "http://www.baidu.com/",
    webSocketDebuggerUrl: "ws://localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F"}

websocket server 端地址:

webSocketDebuggerUrl: "ws://localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F"

建立連接

在任意地址欄中輸入 http://localhost:9222 + devtoolsFrontendUrl 值即可(等同於在當前頁面直接打開 DevTools):

http://localhost:9222/devtools/devtools.html?ws=localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F"

或直接使用 WebSocket 連接,使用 webSocketDebuggerUrl 值連接:

var ws = new WebSocket('ws://localhost:9222/devtools/page/A12A4B08-E5AF-4A84-A86A-A1C86E731D7F"');

注意:每次只能進行一次 WebSocket 連接,之後的連接都會失敗

調用 Command

WebSocket 通道建立完成之後,通過如下方式進行調用:

打開指定頁面,並進行事件監聽(以 Page.loadEventFired 爲例):

ws.onmessage = function(event) {
    console.log(event.data);};ws.send('{"id": 1, "method": "Page.navigate", "params": {"url": "http://www.baidu.com"}}')

獲取到的 loadEventFired 事件數據如下:

{"method": "Page.loadEventFired", "params": {"timestamp": 1402317772.874949}}

更多連接方式

nodejs ws

非常輕量級的 WebSocket 庫,支持 client 端和 server 端,使用方式基本同 HTML5 的標準 WebSocket 庫

client 示例:

var WebSocket = require('ws');var ws = new WebSocket('ws://www.host.com/path');ws.on('open', function() {
    ws.send('something');});ws.on('message', function(data, flags) {
    // flags.binary will be set if a binary data is received
    // flags.masked will be set if the data was masked});

server 示例:

var WebSocketServer = require('ws').Server
      , wss = new WebSocketServer({port: 8080});
    wss.on('connection', function(ws) {
        ws.on('message', function(message) {
            console.log('received: %s', message);
        });
        ws.send('something');
    });

請移步:官方 ws 庫

nodejs chrome-remote-interface

一個實現了 remote debugging protocol 的 nodejs 庫,其中 WebSocket 使用的是 ws 庫,使用方便,推薦使用

示例代碼:

var Chrome = require('chrome-remote-interface');Chrome(function (chrome) {
    with (chrome) {
        on('Page.loadEventFired', function(time) {
            send('Runtime.evaluate',{'expression': 'chrome.loadTimes()', returnByValue: true}, function(err, result) {
                //console.log(err, result );
            });
        });
        Page.enable();
        Page.navigate({'url': 'http://www.baidu.com'});
    }});

請移步:官方 chrome-remote-interface

nodejs socket.io

功能強大,支持集成 WebSocket 服務器端和 Express3 框架與一身,使用簡單,有興趣者請移步:官方 socket.io

WebSocket

協議

它是 HTML5 一種新的協議,實現了瀏覽器與服務器全雙工通信,只需要一個握手動作,瀏覽器和服務器之間就形成了一個快速通道,然後進行數據互傳。

優點:

1、交互時的 header 只有約 2Bytes
2、服務端可以主動推送數據給客戶端

header 格式(握手時):

request:

Cache-Control:no-cache
Connection:Upgrade
Host:localhost:9222
Origin:http://family.baidu.com
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
Sec-WebSocket-Key:TKSQVug6zSIH4uzIyTYBcg==
Sec-WebSocket-Version:13
Upgrade:websocket
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1914.0 Safari/537.36

response:

Connection:Upgrade
Sec-WebSocket-Accept:HyjfMUpyYgWgkYLn/vDDf6rZLuk=
Upgrade:WebSocket

header 格式(交互時):

request:

User-Agent: Fiddler
Content-Type: application/json; charset=utf-8
Host: fakewebsocket
Content-Length: 211

response:

FiddlerTemplate: True
Date: Fri, 25 Jan 2013 16:49:29 GMT
Content-Length: 51

查看 WebSocket 連接

DevTools

直接使用 DevTools,在控制檯建立 WebSocket 連接並交互,在 Network 面板中直接顯示

fiddler

fiddler

自定義 fiddler 的規則,根據 WebSocket 特徵提取信息並僞造 WebSocket 結構數據

因爲僞造時,host 爲 fakewebsocket,無法識別,所以通過 AutoResponder 僞造 respose 數據


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章