Node.js HTTP 解析器 llhttp 的使用

开发 前端
llhttp 是 Node.js 的 HTTP 1.1 解析器,用于替代早期的http_parser,性能上有了非常大的提升,最近打算在 No.js 里引入 llhttp 来处理 HTTP 协议的解析,本文简单介绍一下如何使用。

[[427068]]

llhttp 是 Node.js 的 HTTP 1.1 解析器,用于替代早期的http_parser,性能上有了非常大的提升,最近打算在 No.js 里引入 llhttp 来处理 HTTP 协议的解析,本文简单介绍一下如何使用。

llhttp 项目是 Node.js 中的子项目,地址在:

https://github.com/nodejs/llhttp。

使用步骤如下:

1. 安装 npx:npm i npx -g

2. 执行 ts 生成 c 代码:npx ts-node bin/generate.ts,或者执行 make generate

3. 这时候build 目录下生成了 llhttp.h 和 llhttp.c,再加上 native 下的 c 代码,就是 llhttp 的全部代码,我们可以把他复制到自己的项目中使用

下面看看如何使用。llhttp 使用回调钩子的设计思想,初始化解析器的时候,我们可以设置解析类型,是请求或响应报文,然后设置解析状态的回调,比如解析道 URL 时回调,解析到 header 时回调。接着传入报文执行 llhttp_execute 就可以,下面是解析请求报文的例子。

#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 charat, 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 charat, 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 charat, 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 charat, 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; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.

接着看解析响应的例子。

#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 charat, 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 charat, 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 charat, 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 charat, 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 charat, 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; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.

llhttp 目前支持以下钩子回调。

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;}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.

我们也可以以静态库或动态库的方式使用 llhttp。执行 make all 就会在 build 目录下生成静态和动态库,我们把头文件 llhttp.h 和 静态库或动态库复制到自己项目里使用就可以,编译的时候加上 -lllhttp -L.。

 

总结:llhttp 的使用上还算比较简单清晰,如果我们项目里需要解析 HTTP 协议的话可以试试,使用 demo 可以参考 https://github.com/theanarkh/llhttp-demo。

 

责任编辑:武晓燕 来源: 编程杂技
相关推荐

2017-08-17 13:56:30

JavascriptNode.jsHttp

2023-06-30 23:25:46

HTTP模块内存

2014-11-04 09:54:00

Node.jsWeb

2022-08-28 16:30:34

Node.jsDocker指令

2020-12-08 06:28:47

Node.js异步迭代器

2014-09-12 10:35:09

Node.jsHTTP 206

2013-11-01 09:34:56

Node.js技术

2023-01-10 14:11:26

2015-03-10 10:59:18

Node.js开发指南基础介绍

2021-03-04 23:12:57

Node.js异步迭代器开发

2016-09-18 16:04:24

HTTPNode应用

2021-10-23 06:42:46

Node.js 抓取堆快照.js

2017-04-24 08:31:26

Node.jsExpress.jsHTTP

2020-05-29 15:33:28

Node.js框架JavaScript

2020-02-25 12:27:59

Node.jsWeb开发前端

2021-01-14 10:48:34

Docker CompNode.js开发

2021-12-25 22:29:57

Node.js 微任务处理事件循环

2021-11-11 11:13:20

js Npm基础

2012-02-03 09:25:39

Node.js

2020-10-12 08:06:28

HTTP 服务器证书
点赞
收藏

51CTO技术栈公众号