Node.js HTTP 解析器 llhttp 的使用
llhttp 是 Node.js 的 HTTP 1.1 解析器,用于替代早期的http_parser,性能上有了非常大的提升,最近打算在 No.js 里引入 llhttp 來處理 HTTP 協(xié)議的解析,本文簡單介紹一下如何使用。
llhttp 項(xiàng)目是 Node.js 中的子項(xiàng)目,地址在:
https://github.com/nodejs/llhttp。
使用步驟如下:
1. 安裝 npx:npm i npx -g
2. 執(zhí)行 ts 生成 c 代碼:npx ts-node bin/generate.ts,或者執(zhí)行 make generate
3. 這時(shí)候build 目錄下生成了 llhttp.h 和 llhttp.c,再加上 native 下的 c 代碼,就是 llhttp 的全部代碼,我們可以把他復(fù)制到自己的項(xiàng)目中使用
下面看看如何使用。llhttp 使用回調(diào)鉤子的設(shè)計(jì)思想,初始化解析器的時(shí)候,我們可以設(shè)置解析類型,是請求或響應(yīng)報(bào)文,然后設(shè)置解析狀態(tài)的回調(diào),比如解析道 URL 時(shí)回調(diào),解析到 header 時(shí)回調(diào)。接著傳入報(bào)文執(zhí)行 llhttp_execute 就可以,下面是解析請求報(bào)文的例子。
- #include <stdio.h>
 - #include <string.h>
 - #include "llhttp.h"
 - #define MAX_LEN 2048
 - int on_message_begin(llhttp_t* parser){
 - printf("parse start\n");
 - return 0;
 - }
 - int on_url(llhttp_t* parser, const char* at, size_t length){
 - char url[MAX_LEN];
 - strncpy(url, at, length);
 - url[length] = '\0';
 - printf("on_url: %s\n", url);
 - return 0;
 - }
 - int on_header_field(llhttp_t* parser, const char* at, size_t length){
 - char header_field[MAX_LEN];
 - strncpy(header_field, at, length);
 - header_field[length] = '\0';
 - printf("head field: %s\n", header_field);
 - return 0;
 - }
 - int on_header_value(llhttp_t* parser, const char* at, size_t length){
 - char header_value[MAX_LEN];
 - strncpy(header_value, at, length);
 - header_value[length] = '\0';
 - printf("head value: %s\n", header_value);
 - return 0;
 - }
 - int on_headers_complete(llhttp_t* parser){
 - printf("on_headers_complete, major: %d, major: %d, keep-alive: %d, upgrade: %d\n", parser->http_major, parser->http_minor, llhttp_should_keep_alive(parser), parser->upgrade);
 - return 0;
 - }
 - int on_body(llhttp_t* parser, const char* at, size_t length){
 - char body[MAX_LEN];
 - strncpy(body, at, length);
 - body[length] = '\0';
 - printf("on_body: %s\n", body);
 - return 0;
 - }
 - int on_message_complete(llhttp_t* parser){
 - printf("on_message_complete\n");
 - return 0;
 - }
 - int main(){
 - llhttp_t parser;
 - llhttp_settings_t settings;
 - llhttp_settings_init(&settings);
 - llhttp_init(&parser, HTTP_REQUEST, &settings);
 - settings.on_message_begin = on_message_begin;
 - settings.on_url = on_url;
 - settings.on_header_field = on_header_field;
 - settings.on_header_value = on_header_value;
 - settings.on_headers_complete = on_headers_complete;
 - settings.on_body = on_body;
 - settings.on_message_complete = on_message_complete;
 - const char* request = "POST /index.html HTTP/1.1\r\nconnection:close\r\ncontent-length: 1\r\n\r\n1\r\n\r\n";
 - int request_len = strlen(request);
 - enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
 - if (err != HPE_OK) {
 - fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
 - parser.reason);
 - }
 - return 0;
 - }
 
接著看解析響應(yīng)的例子。
- #include <stdio.h>
 - #include <string.h>
 - #include "llhttp.h"
 - #define MAX_LEN 2048
 - int on_message_begin(llhttp_t* parser){
 - printf("parse start\n");
 - return 0;
 - }
 - int on_url(llhttp_t* parser, const char* at, size_t length){
 - char url[MAX_LEN];
 - strncpy(url, at, length);
 - url[length] = '\0';
 - printf("on_url: %s\n", url);
 - return 0;
 - }
 - int on_status(llhttp_t* parser, const char* at, size_t length){
 - char status[MAX_LEN];
 - strncpy(status, at, length);
 - status[length] = '\0';
 - printf("on_status: %s\n", status);
 - return 0;
 - }
 - int on_header_field(llhttp_t* parser, const char* at, size_t length){
 - char header_field[MAX_LEN];
 - strncpy(header_field, at, length);
 - header_field[length] = '\0';
 - printf("head field: %s\n", header_field);
 - return 0;
 - }
 - int on_header_value(llhttp_t* parser, const char* at, size_t length){
 - char header_value[MAX_LEN];
 - strncpy(header_value, at, length);
 - header_value[length] = '\0';
 - printf("head value: %s\n", header_value);
 - return 0;
 - }
 - int on_headers_complete(llhttp_t* parser){
 - printf("on_headers_complete, major: %d, major: %d, keep-alive: %d, upgrade: %d\n", parser->http_major, parser->http_minor, llhttp_should_keep_alive(parser), parser->upgrade);
 - return 0;
 - }
 - int on_body(llhttp_t* parser, const char* at, size_t length){
 - char body[MAX_LEN];
 - strncpy(body, at, length);
 - body[length] = '\0';
 - printf("on_body: %s\n", body);
 - return 0;
 - }
 - int on_message_complete(llhttp_t* parser){
 - printf("on_message_complete\n");
 - return 0;
 - }
 - int main(){
 - llhttp_t parser;
 - llhttp_settings_t settings;
 - llhttp_settings_init(&settings);
 - llhttp_init(&parser, HTTP_RESPONSE, &settings);
 - settings.on_message_begin = on_message_begin;
 - settings.on_url = on_url;
 - settings.on_status = on_status;
 - settings.on_header_field = on_header_field;
 - settings.on_header_value = on_header_value;
 - settings.on_headers_complete = on_headers_complete;
 - settings.on_body = on_body;
 - settings.on_message_complete = on_message_complete;
 - const char* reponse = "HTTP/1.1 200 OK\r\nServer: nginx\r\ncontent-length: 11\r\n\r\nhello:world\r\n\r\n";
 - int reponse_len = strlen(reponse);
 - enum llhttp_errno err = llhttp_execute(&parser, reponse, reponse_len);
 - if (err != HPE_OK) {
 - fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
 - parser.reason);
 - }
 - return 0;
 - }
 
llhttp 目前支持以下鉤子回調(diào)。
- struct llhttp_settings_s {
 - /* Possible return values 0, -1, `HPE_PAUSED` */
 - llhttp_cb on_message_begin;
 - /* Possible return values 0, -1, HPE_USER */
 - llhttp_data_cb on_url;
 - llhttp_data_cb on_status;
 - llhttp_data_cb on_header_field;
 - llhttp_data_cb on_header_value;
 - /* Possible return values:
 - * 0 - Proceed normally
 - * 1 - Assume that request/response has no body, and proceed to parsing the
 - * next message
 - * 2 - Assume absence of body (as above) and make `llhttp_execute()` return
 - * `HPE_PAUSED_UPGRADE`
 - * -1 - Error
 - * `HPE_PAUSED`
 - */
 - llhttp_cb on_headers_complete;
 - /* Possible return values 0, -1, HPE_USER */
 - llhttp_data_cb on_body;
 - /* Possible return values 0, -1, `HPE_PAUSED` */
 - llhttp_cb on_message_complete;
 - /* When on_chunk_header is called, the current chunk length is stored
 - * in parser->content_length.
 - * Possible return values 0, -1, `HPE_PAUSED`
 - */
 - llhttp_cb on_chunk_header;
 - llhttp_cb on_chunk_complete;
 - /* Information-only callbacks, return value is ignored */
 - llhttp_cb on_url_complete;
 - llhttp_cb on_status_complete;
 - llhttp_cb on_header_field_complete;
 - llhttp_cb on_header_value_complete;};
 
我們也可以以靜態(tài)庫或動(dòng)態(tài)庫的方式使用 llhttp。執(zhí)行 make all 就會在 build 目錄下生成靜態(tài)和動(dòng)態(tài)庫,我們把頭文件 llhttp.h 和 靜態(tài)庫或動(dòng)態(tài)庫復(fù)制到自己項(xiàng)目里使用就可以,編譯的時(shí)候加上 -lllhttp -L.。
總結(jié):llhttp 的使用上還算比較簡單清晰,如果我們項(xiàng)目里需要解析 HTTP 協(xié)議的話可以試試,使用 demo 可以參考 https://github.com/theanarkh/llhttp-demo。















 
 
 










 
 
 
 