1、前言
目前,HTTP協議是網際網路上應用最為廣泛的一種網路協議,也是前端er接觸最多的一種協議。透過閱讀http模組在nodejs中的實現,能夠更深入的瞭解HTTP協議。HTTP協議是基於TCP協議之上的應用層協議,它的實現離不開TCP/IP協議族。而具體到程式碼實現,http模組依賴於net模組。
如下圖所示:在nodejs中,http透過net模組傳輸資料,得到資料之後依靠HTTP_PARSER對資料進行解析。
nodejs中啟動一個HTTP服務很簡單,就是例項化一個Server物件,並且監聽某個埠:
Server類繼承於net.Server,並監聽’connection‘事件。
在Server類中,主要做了兩件事:
初始化NET模組並建立TCP網路監聽
監聽自身的request事件。
當客戶端請求到來的時候,Server例項會首先監聽到 "connection" 事件,建立起TCP連線並在connectionListener中暴露出socket物件。接下來,HTTP模組就透過socket物件與客戶端進行資料互動。
當一個請求到來後,Server會觸發自身的 request 事件,呼叫 requestListener 方法,即建立Server例項時傳入的回撥函式。
注:socket物件類似於TCP協議的一個實現,可以透過它與客戶端進行資料互動;
在 connectionListener 函式中,還初始化了parser例項,並給它綁定了一個 onIncoming 函式 HTTP Parser;整個解析流程在 connectionListener 中進行,socket 透過 "data" 事件獲取TCP推入的資料。
當socket獲取到資料之後,會先對資料進行解析,即:parser.excute(),解析工具是parser。值得說明的是,作者為了實現對 parser 的重用, parser是從一個"FreeList池"中獲取的。
Parser(common.js) 繼承自 HTTPParser(node_http_parser.cc),並綁定了4個解析週期回撥函式:parserOnHeaders、parserOnHeadersComplete、parserOnBody、parserOnMessageComplete。
在執行 parser.execute 過程中,HTTPParser 會在解析週期內回撥這些函式。
1、TCP資料到達時, 先執行execute()
2、順藤摸瓜,我們發現parser.excute 就是 Excute(node_http_parser.cc)。而Excute也只是一個外包而已,具體工作是http_parser_excute(http_parser.c)搞定的。
3、http_parser.c只有兩類回撥:HTTP_CB、HTTP_DATA_CB。透過過載的方式,在這兩類函式中註冊了8個週期函式,如下圖:
4、雖然http_parser註冊有8個回撥函式,但 node_http_parser.cc 對外只暴露出四個週期函式:
parserOnHeaders
parserOnHeadersComplete
parserOnBody
parserOnMessageComplete
5、當 http_parser.c 解析到 on_headers_complete 時,執行HTTP_CB(on_headers_complete)回撥函式,如圖:
函式內會執行 kOnHeadersComplete 回撥函式,即:parserOnHeadersComplete 函式(common.js)
6、此時請求頭解析基本完成,接下來建立一個IncomingMessage的例項,然後把請求頭資料包裝到該例項上。執行 onIncoming 回撥函式,並把得到的IncomingMessage例項作為引數傳遞進去。
7、 在 parserOnIncoming 中,建立一個ServerResponse例項。
具備了req、res兩個例項,接下來觸發Server監聽的 request 事件。
在 Server 例項化時的,requestListener是作為函式引數對 request 事件進行監聽的。
8、回到Server建立時:
綜上所述,http_parser 解析完 header 之後,就會觸發 request 事件。
那body資料放到哪裡呢,其實body資料會一直放到流裡面,直到使用者使用data事件接收資料。也就是說,觸發request的時候,body並不會被解析。
完整的http請求是這樣的:
客戶端發起HTTP請求,首先觸發Server端的connection事件,建立TCP連結。
Server接收到connection事件後,建立TCP連線,並暴露出套接字,透過套接字監聽"data"事件;初始化http-parser,為後續解析資料備用。
HTTP請求資料到達Server端,parser執行execute方法進行解析,請求頭解析成功後,透過回撥觸發request事件。
至此,我們在Server回撥函式中,就接收到了此次http請求的request
由於nodejs不少底層庫都是C++/C編寫的,在閱讀、除錯的過程中非常不便。我自己在讀原始碼的時候,也只是著重看的JS部分原始碼。比如,TCP的三次握手、四次揮手,就沒深究它的實現細節啦。 以上分析沒有涉及到http-body的解析,對於有body的網路請求,實際情況要更加複雜一些,還有一些細節沒有完全搞清。等下次總結、分享,我會盡量把漏掉細節都補上。
1、前言
目前,HTTP協議是網際網路上應用最為廣泛的一種網路協議,也是前端er接觸最多的一種協議。透過閱讀http模組在nodejs中的實現,能夠更深入的瞭解HTTP協議。HTTP協議是基於TCP協議之上的應用層協議,它的實現離不開TCP/IP協議族。而具體到程式碼實現,http模組依賴於net模組。
如下圖所示:在nodejs中,http透過net模組傳輸資料,得到資料之後依靠HTTP_PARSER對資料進行解析。
2、原始碼啟動一個HTTP服務nodejs中啟動一個HTTP服務很簡單,就是例項化一個Server物件,並且監聽某個埠:
SERVER類Server類繼承於net.Server,並監聽’connection‘事件。
在Server類中,主要做了兩件事:
初始化NET模組並建立TCP網路監聽
監聽自身的request事件。
當客戶端請求到來的時候,Server例項會首先監聽到 "connection" 事件,建立起TCP連線並在connectionListener中暴露出socket物件。接下來,HTTP模組就透過socket物件與客戶端進行資料互動。
當一個請求到來後,Server會觸發自身的 request 事件,呼叫 requestListener 方法,即建立Server例項時傳入的回撥函式。
注:socket物件類似於TCP協議的一個實現,可以透過它與客戶端進行資料互動;
在 connectionListener 函式中,還初始化了parser例項,並給它綁定了一個 onIncoming 函式 HTTP Parser;整個解析流程在 connectionListener 中進行,socket 透過 "data" 事件獲取TCP推入的資料。
當socket獲取到資料之後,會先對資料進行解析,即:parser.excute(),解析工具是parser。值得說明的是,作者為了實現對 parser 的重用, parser是從一個"FreeList池"中獲取的。
Parser(common.js) 繼承自 HTTPParser(node_http_parser.cc),並綁定了4個解析週期回撥函式:parserOnHeaders、parserOnHeadersComplete、parserOnBody、parserOnMessageComplete。
在執行 parser.execute 過程中,HTTPParser 會在解析週期內回撥這些函式。
解析的具體流程1、TCP資料到達時, 先執行execute()
2、順藤摸瓜,我們發現parser.excute 就是 Excute(node_http_parser.cc)。而Excute也只是一個外包而已,具體工作是http_parser_excute(http_parser.c)搞定的。
3、http_parser.c只有兩類回撥:HTTP_CB、HTTP_DATA_CB。透過過載的方式,在這兩類函式中註冊了8個週期函式,如下圖:
4、雖然http_parser註冊有8個回撥函式,但 node_http_parser.cc 對外只暴露出四個週期函式:
parserOnHeaders
parserOnHeadersComplete
parserOnBody
parserOnMessageComplete
5、當 http_parser.c 解析到 on_headers_complete 時,執行HTTP_CB(on_headers_complete)回撥函式,如圖:
函式內會執行 kOnHeadersComplete 回撥函式,即:parserOnHeadersComplete 函式(common.js)
6、此時請求頭解析基本完成,接下來建立一個IncomingMessage的例項,然後把請求頭資料包裝到該例項上。執行 onIncoming 回撥函式,並把得到的IncomingMessage例項作為引數傳遞進去。
7、 在 parserOnIncoming 中,建立一個ServerResponse例項。
具備了req、res兩個例項,接下來觸發Server監聽的 request 事件。
在 Server 例項化時的,requestListener是作為函式引數對 request 事件進行監聽的。
8、回到Server建立時:
綜上所述,http_parser 解析完 header 之後,就會觸發 request 事件。
那body資料放到哪裡呢,其實body資料會一直放到流裡面,直到使用者使用data事件接收資料。也就是說,觸發request的時候,body並不會被解析。
3、流程梳理完整的http請求是這樣的:
客戶端發起HTTP請求,首先觸發Server端的connection事件,建立TCP連結。
Server接收到connection事件後,建立TCP連線,並暴露出套接字,透過套接字監聽"data"事件;初始化http-parser,為後續解析資料備用。
HTTP請求資料到達Server端,parser執行execute方法進行解析,請求頭解析成功後,透過回撥觸發request事件。
至此,我們在Server回撥函式中,就接收到了此次http請求的request
4、結語由於nodejs不少底層庫都是C++/C編寫的,在閱讀、除錯的過程中非常不便。我自己在讀原始碼的時候,也只是著重看的JS部分原始碼。比如,TCP的三次握手、四次揮手,就沒深究它的實現細節啦。 以上分析沒有涉及到http-body的解析,對於有body的網路請求,實際情況要更加複雜一些,還有一些細節沒有完全搞清。等下次總結、分享,我會盡量把漏掉細節都補上。