http解析库http-parser

2023-05-16

一、http-parser简介

1、简介

       http-parser是一个用C编写的HTTP消息解析器,可以解析请求和响应,被设计用于高性能HTTP应用程序。它不会进行任何系统调用及内存分配,它不会缓冲数据,它可以被随时中断。根据你的体系结构,每个消息流只需要大约40个字节的数据(在每个连接的web服务器中。

2、特征

  • 不依赖第三方库
  • 处理持续流
  • 分块解码
  • 支持Upgrade
  • 防止缓冲区溢出攻击

3、可以从HTTP消息中解析下列信息

  • 报头域及值(Header fields and values)
  • 内容长度(Content-Length)
  • 请求方法
  • 响应状态码
  • 传输编码
  • HTTP版本
  • 请求URL(网址)
  • 消息体(Message body)

 

二、用法

1、下载

       http-parser官方地址:https://github.com/nodejs/http-parser

解压:

unzip http-parser-master.zip

2、编译安装

make
make parsertrace
make url_parser
sudo make install

3、初始化

       http-parser的每个tcp连接使用一个对象。使用初始化结构http_parser_init()并设置回调。初始化结构如下:

void http_parser_settings_init(http_parser_settings *settings);

    对于请求解析器可能是这样的:

http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;

4、执行并错误检查

    当套接字接收到数据时,执行解析器并检查错误。执行器函数如下

size_t http_parser_execute(http_parser *parser,
                           const http_parser_settings *settings,
                           const char *data,
                           size_t len);

例:

size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;

recved = recv(fd, buf, len, 0);

if (recved < 0) {
  /* Handle error. */
}

/* Start up / continue the parser.
 * Note we pass recved==0 to signal that EOF has been received.
 */
nparsed = http_parser_execute(parser, &settings, buf, recved);

if (parser->upgrade) {
  /* handle new protocol */
} else if (nparsed != recved) {
  /* Handle error. Usually just close the connection. */
}

       http_parser需要知道流的结尾在哪里。例如,一些服务发送的请求没有Content-Length并希望客户端输入知道EOF结束。为了告诉http_parser关于EOF的信息,请将0作为http_parser_execute()的第四个参数。在EOF期间仍然会遇到回调和错误,因此必须做好接收它们的准备。

       报头消息信息(如状态码、方法和http版本)存储在解析器结构中。此数据仅临时存储在http_parser中,并在每个新消息上重置。如果后续需要此信息,可在headers_complete期间回调。

       解析器透明地解码请求和响应的传输编码。即,分块编码在被发送到on_body回调之前被解码。

 

三、升级的特殊问题

       http_parser支持将连接升级到不同的协议。一个常见的例子就是websocket协议,它发送一个请求,比如:

 GET /demo HTTP/1.1
    Upgrade: WebSocket
    Connection: Upgrade
    Host: example.com
    Origin: http://example.com
    WebSocket-Protocol: sample

       为了支持此功能,解析器将其视作为没有正文的普通http消息,同时发出on_headers_complete和on_message_complete回调。但是,http_parser_execute()将在头的末尾停止解析并返回。

       在http_parser_execute()返回后,用户需要检查parser->upgrade是否设置为1。非http数据从http_parser_execute()返回值提供的缓冲区偏移量开始。

 

四、回调

       在http_parser_execute()调用期间,将执行http_parser_settings中设置的回调。解析器保持状态,从不向后看,因此不需要缓存数据。如果你需要保持某些数据以供以后使用,可从回调中执行此操作。回调有两种类型:

  • 通知:typedef int (*http_cb) (http_parser*); 回调:on_message_begin, on_headers_complete, on_message_complete.
  • 数据:typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); 回调:(requests only) on_url, (common) on_header_field, on_header_value, on_body;

成功时回调必须返回0。返回非零值表示解析器出错,使其立即退出。

       对于需要向回调传递本地信息或从回调传递本地信息的情况,可以使用http_parser对象的数据字段。这种情况的一个例子是使用线程处理套接字连接、解析请求,然后通过该套接字给出响应。通过实例化包含相关数据的线程本地结构(例如,接受的套接字、分配给回调写入的内存等),解析器的回调能够以线程安全的方式在线程的作用域和回调的作用域之间传递数据。这允许在多线程上下文中使用http_parser。

例:

typedef struct {
  socket_t sock;
  void* buffer;
  int buf_len;
 } custom_data_t;


int my_url_callback(http_parser* parser, const char *at, size_t length) {
  /* access to thread local custom_data_t struct.
  Use this access save parsed data for later use into thread local
  buffer, or communicate over socket
  */
  parser->data;
  ...
  return 0;
}

...

void http_parser_thread(socket_t sock) {
 int nparsed = 0;
 /* allocate memory for user data */
 custom_data_t *my_data = malloc(sizeof(custom_data_t));

 /* some information for use by callbacks.
 * achieves thread -> callback information flow */
 my_data->sock = sock;

 /* instantiate a thread-local parser */
 http_parser *parser = malloc(sizeof(http_parser));
 http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
 /* this custom data reference is accessible through the reference to the
 parser supplied to callback functions */
 parser->data = my_data;

 http_parser_settings settings; /* set up callbacks */
 settings.on_url = my_url_callback;

 /* execute parser */
 nparsed = http_parser_execute(parser, &settings, buf, recved);

 ...
 /* parsed information copied from callback.
 can now perform action on data copied into thread-local memory from callbacks.
 achieves callback -> thread information flow */
 my_data->buffer;
 ...
}

       如果你将HTTP消息分块解析(例,socket中的read()请求行、解析,读取一半头等),你的数据回调可能会被多次调用。http_parser保证数据指针只在回调的生存期内有效。如果适合应用程序,还可读取(read())堆中的缓冲区,以避免复制内存。

       如果读取/解析部分头,那么读取头将是一件棘手的任务。基本上,你需要记住最后一个头回调是字段还是值,并应用一下逻辑:

(on_header_field and on_header_value shortened to on_h_*)
 ------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback   | Description/action                         |
 ------------------------ ------------ --------------------------------------------
| nothing (first call)   | on_h_field | Allocate new buffer and copy callback data |
|                        |            | into it                                    |
 ------------------------ ------------ --------------------------------------------
| value                  | on_h_field | New header started.                        |
|                        |            | Copy current name,value buffers to headers |
|                        |            | list and allocate new buffer for new name  |
 ------------------------ ------------ --------------------------------------------
| field                  | on_h_field | Previous name continues. Reallocate name   |
|                        |            | buffer and append callback data to it      |
 ------------------------ ------------ --------------------------------------------
| field                  | on_h_value | Value for current header started. Allocate |
|                        |            | new buffer and copy callback data to it    |
 ------------------------ ------------ --------------------------------------------
| value                  | on_h_value | Value continues. Reallocate value buffer   |
|                        |            | and append callback data to it             |
 ------------------------ ------------ --------------------------------------------

另:http_parser_parse_url()提供了一个简单的零拷贝url解析器。此库的用户可能希望使用它来解析从on_url构造的url。

 

五、实例

#include <http_parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>

using namespace std;

static http_parser *parser;

int on_message_begin(http_parser* _) {
	(void)_;
	printf("\n***MESSAGE BEGIN***\n\n");
	return 0;
}

int on_headers_complete(http_parser* _) {
	(void)_;
	printf("\n***HEADERS COMPLETE***\n\n");
	return 0;
}

int on_message_complete(http_parser* _) {
	(void)_;
	printf("\n***MESSAGE COMPLETE***\n\n");
	return 0;
}

int on_url(http_parser* _, const char* at, size_t length) {
	(void)_;
	printf("Url: %.*s\n", (int)length, at);
	return 0;
}

int on_status(http_parser* _, const char* at, size_t length) {
	(void)_;
	printf("Status: %.*s\n", (int)length, at);
	return 0;
}

int on_header_field(http_parser* _, const char* at, size_t length) {
	(void)_;
	printf("Header field: %.*s\n", (int)length, at);
	return 0;
}

int on_header_value(http_parser* _, const char* at, size_t length) {
	(void)_;
	printf("Header value: %.*s\n", (int)length, at);
	return 0;
}

int on_body(http_parser* _, const char* at, size_t length) {
	(void)_;
	printf("Body: %.*s\n", (int)length, at);
	return 0;
}

int on_chunk_header(http_parser* _) {
	(void)_;
	printf("\n***CHUNK HEADER***\n\n");
	return 0;
}

int on_chunk_complete(http_parser* _) {
	(void)_;
	printf("\n***CHUNK COMPLETE***\n\n");
	return 0;
}

// http_parser的回调函数,需要获取HEADER后者BODY信息,可以在这里面处理
// 注意其中变量前面“.”表示的是当前结构体中的成员变量,
// 类似于对象.成员,同时可以可以乱序,如果未指定则必须要按原先的顺序
static http_parser_settings settings_null = {
	.on_message_begin = on_message_begin,	// 相当于settings_null.on_message_begin = on_message_begin,
	.on_url = on_url,
	.on_status = on_status,
	.on_header_field = on_header_field,
	.on_header_value = on_header_value,
	.on_headers_complete = on_headers_complete,
	.on_body = on_body,
	.on_message_complete = on_message_complete,
	.on_chunk_header = on_chunk_header,
	.on_chunk_complete = on_chunk_complete
};

int main(void)
{
	const char* buf;
	float start, end;
	size_t parsed;

	parser = (http_parser *)malloc(sizeof(http_parser));
	buf =  "GET http://admin.omsg.cn/uploadpic/2016121034000012.png HTTP/1.1\r\nHost: \
		admin.omsg.cn\r\nAccept:*/*\r\nConnection: Keep-Alive\r\n\r\n";

	start = (float)clock() / CLOCKS_PER_SEC;
	
	for (int i = 0; i < 1; i++) {
		// 初始化parser为Request类型
		http_parser_init(parser, HTTP_REQUEST);	
		// 执行解析过程
		parsed = http_parser_execute(parser, &settings_null, buf, strlen(buf));
	}

	end = (float)clock() / CLOCKS_PER_SEC;

	buf = "HTTP/1.1 200 OK\r\n"
		"Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
		"Server: Apache\r\n"
		"X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
		"Content-Type: text/xml; charset=utf-8\r\n"
		"Connection: close\r\n"
		"\r\n"
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
		"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
		"  <SOAP-ENV:Body>\n"
		"    <SOAP-ENV:Fault>\n"
		"       <faultcode>SOAP-ENV:Client</faultcode>\n"
		"       <faultstring>Client Error</faultstring>\n"
		"    </SOAP-ENV:Fault>\n"
		"  </SOAP-ENV:Body>\n"
		"</SOAP-ENV:Envelope>";

	// 初始化parser为Response
	http_parser_init(parser, HTTP_RESPONSE);
	// 执行解析过程
	parsed = http_parser_execute(parser, &settings_null, buf, strlen(buf));

	free(parser);
	parser = NULL;

	printf("Elapsed %f seconds.\n", (end - start));

	return 0;

}

编译:

gcc -Wall -Wextra -Werror -Wno-error=unused-but-set-variable -O3 http-parser-master/http_parser.o demo.cpp -o demo

执行结果:

参考:https://github.com/nodejs/http-parser

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

http解析库http-parser 的相关文章

  • 为什么大多数 API 分页不依赖 HTTP Range 标头?

    我搜索了很多 但找不到这个问题的好的答案 作为 HATEOAS 爱好者 我认为这个标题非常适合 Range item 1 20 100 在HTTP规范中 我不明白一些 矛盾 范围单位可以接受 其他范围单位 range unit bytes
  • 重定向后,curl 对所有请求使用 POST

    根据文档和一些类似的问题 SOcurl 应该遵循使用 GET 方法的重定向 除非将 post30x 指定为参数 但这是我测试的结果 curl kvv b tmp tmp BEo6w3GKDq c tmp tmp BEo6w3GKDq X P
  • 如何让 Symfony 2 采用协议方案(http vs https)

    我有一个 Symfony 2 网站 在开发中运行在 HTTP 上 在生产中运行在 HTTPS 上 我注意到在生产中 Symfony 生成的 URL 仍然全部呈现为 HTTP 我怎么也可以 让框架采用当前提供网站的协议 可能是首选 或者 仅在
  • Android Http 获取会话 Cookie

    我本来不想在这里发帖 因为网上有太多信息 但我已经深入搜寻但无法弄清楚 好吧 所以我无法让它在两种情况下工作 希望这两种情况的答案是相同的 我的问题是我设置了请求标头 但它似乎没有发送它 我有一个会话 id s e32ff223fwefd3
  • AngularJS 如何防止重复的http请求?

    过去的一天我一直在为一些奇怪的情况而苦苦挣扎 发生的情况是 对于远程服务器上的 API 的 http 请求 偶尔会发送重复的请求 任何人都可以提供有关如何避免这些重复请求的帮助吗 这是我在工厂中使用的函数的示例 factory getAll
  • 如何发送http basic auth post?

    我的任务是使用基本身份验证创建 http 帖子 我正在 asp net MVC 应用程序中使用 C 进行开发 我也得到过这个例子 POST v2 token endpoint HTTP 1 1 Authorization Basic Y2x
  • 使用 PHP 的 HTTP PUT、DELETE 和 I/O 流

    除了 HTTP PUT 方法之外 还有什么方法可以访问通过 HTTP PUT 方法发送的数据 putdata fopen php input r 我从未与PUT and DELETE方法和 putdata fopen php input r
  • 使用凭证进行跨源资源共享

    我有一个跨多个子域 example com blog example com 和 app example com 的通用身份验证表单 登录表单必须将此数据提交到 example com 无论它显示在哪里 所以我想到使用 CORS 但是这样
  • NSURLSession 帖子:uploadTask 和 dataTask 之间的区别

    这是我的两个例子 let config NSURLSessionConfiguration defaultSessionConfiguration config HTTPAdditionalHeaders Accept applicatio
  • PHP 空 $_POST

    我通过 HTTP POST 向 PHP 发送数据 这对于短于 8MB 8192KB 的数据来说效果很好 但是当发送的数据量更大时 PHP 会显示 POST变量为空 我强调的是 POST变量甚至不包含帖子字段的名称 它作为空数组存在 临界点似
  • 如何缓存WKWebView加载的资源?

    I use WKWebView显示包括图像在内的富文本 我想将这些图像缓存在磁盘上 如何获取这些 HTTP 请求并缓存响应 默认设置对 WKWebView 中加载的所有资源进行缓存 您所要做的就是在 HTTP 响应中为这些资源设置正确的标头
  • 网页编码,设置矛盾[重复]

    这个问题在这里已经有答案了 如果一个网页有 但http标头有 Content Type text html charset UTF 8 那么假设什么编码呢 在 HTML5 中 优先级定义为 用户浏览器设置 字节顺序标记 HTTP 标头 or
  • 为什么 Python 3 http.client 比 python-requests 快这么多?

    我今天测试了不同的 Python HTTP 库 我意识到http client https docs python org 3 library http client html库的执行速度似乎比requests http docs pyth
  • 在 Java 中使用 JSON 的 HTTP POST

    我想在 Java 中使用 JSON 制作一个简单的 HTTP POST 假设网址是www site com 它接受值 name myname age 20 标记为 details 例如 我将如何创建 POST 语法 我似乎也无法在 JSON
  • 如何修改 HttpUrlConnection 的标头

    我试图稍微改进一下 Java Html 文档 但我遇到了问题HttpUrlConntion 有一件事是 如果用户代理是 Java VM 某些服务器会阻止请求 另一个问题是HttpUrlConnection不设置Referrer or Loc
  • 使用 Servlet 启动 VLC HTTP Stream 时出现问题

    我正在为自己开发一个 VLC 项目 我的目标是创建一个 HTML 前端来启动流 我通过使用 Java Servlet 来完成此操作 概述 乌班图13 04 Java 7 21 冰茶 2 3 9 Eclipse JAVAEE IDE 雄猫7
  • Node.js 管道化 HTTP 客户端代理?

    Node js 中内置的 HTTP 客户端似乎不支持管道请求 https stackoverflow com a 5776649 362536 然而 我突然想到 也许可以创建一个Agent https nodejs org api http
  • ResponseNotReady 对于真正简单的 python http 请求?

    我正在用 python 编写一个简单的脚本重放保存的 HTTP 请求 https stackoverflow com questions 8384848 method program for sending a given http req
  • iPhone Mobile Safari,最大并行 http 连接数是多少?

    我想在 iPhone Mobile Safari OS4 中使用并行 AJAX HTTP 请求 最大并行连接数是多少 如果我没记错的话 Safari 最多使用 4 个到同一服务器的连接 但您可以使用以下命令自行测试这个小测试用例 http
  • 在工厂和控制器之间共享 http.get 数据

    我成功创建了一个获取 php 文件输出 JSON 的工厂 我的问题是如何从控制器内访问它 myApp angular module myApp myApp factory mainData http gt http get gethome

随机推荐

  • 【高效】【IDE】VSCode 插件

    Docuemnt This 加注释文档 选中你的函数名字 xff0c 按两次ctrl 43 alt 43 D xff1b Better Comments 注释高亮 Live Server 实时预览页面 Live Server会启动一个本地服
  • 嵌入式软件算法优化

    嵌入式软件算法优化 一 算法优化原则二 算法优化方法1 系统优化2 算法优化 xff08 需要理解算法原理 xff09 3 代码优化4 使用硬件资源 xff08 需要熟悉芯片架构及资源 xff09 5 汇编 一 算法优化原则 xff08 1
  • CAN总线原理简介

    一 xff0e CAN总线简介 xff1a 是一种串行通信协议 xff0c 能有效的支持具有很高安全等级的分布实时控制应用范围十分广泛 xff0c 从高速网络到低价位的多路接线都可以使用CAN主要运用于汽车电子航天等行业 xff0c 使用C
  • freeRTOS 任务切换

    使用PendSV实现任务切换 上下文切换被触发的场合可以是 xff1a 1 执行一个系统调用 2 系统滴答定时器 SysTick 中断 br PendSV中断服务函数 br TaskSelectHighestPrior的两种方法 br br
  • make -j 参数加快编译效率

    对于大型项目 xff0c 在使用cmake控制编译时 xff0c 仅仅执行make指令效率较低 xff0c 使用make j后面跟一个数字 xff0c 比如make j4 make j6 make j14等 含义是 让make最多允许n个编
  • cmake中add_dependencies的基本作用

    假设我们需要生成一个可执行文件 该文件生成需要链接a so b so c so d so四个动态库 正常来讲 我们一把只需要以下两条指令即可 ADD EXECUTABLE span class token punctuation span
  • 命令行给cmake传递参数

    我们期望在编译前将一些信息缓存起来 然后用CMakeLists txt进行构建时 希望可以访问之前缓存给cmake的变量 比如我们希望缓存TARGET CPU 并且他的值为X86 那么我们可以在命令行或者脚本中执行一下操作 cmake DT
  • 在CMakeLists.txt如何执行脚本?execute_process

    execute process span class token punctuation span COMMAND span class token function bash span SCRIPT PATH name sh WORK P
  • C++运算符重载中有些方法为什么需要定义为友元函数

    C 43 43 提供运算符重载主要目的 xff1a 希望对象之间的运算看起来可以和编译器内置类型一样丝滑 xff1b 相当于是告知编译器 xff0c 类对象之间运算应该如何去做处理 通过实现一个复数类 xff0c 来阐述本文章的主题 xff
  • linux网络编程之socket,bind,listen,connect,accept

    socket span class token macro property span class token directive hash span span class token directive keyword include s
  • Linux网络发送和接收内核缓冲区大小的设置

    socket属性 xff1a SO SNDBUF 发送缓冲区 SO SNDBUF Sets or gets the maximum socket send buffer span class token keyword in span by
  • docker查看运行时容器的IP地址

    使用inspect来查看容器的信息 span class token function docker span inspect span class token punctuation span docker name span class
  • python基础梳理(一)

    一 python程序的组成 表达式 xff1a 建立并且处理数据对象且能返回数据对象的引用关系 示例 xff1a 1 43 2 系统会产生1和2俩个对象 xff0c 并且进行处理生产对象3 xff0c 将对象3返回回去 二 核心的数字类型
  • 串级PID结构及参数调整见解

    在设计控制系统中 xff0c 常用的控制算法为PID xff0c 即比例 积分 微分控制器 能够实现对控制对象的物理特性的控制 xff0c 以期达到特定的运行效果 此外由于PID控制器的灵活特性 xff0c 可以与其它控制算法进行灵活的组合
  • freeRTOS 开启关闭调度器、挂起恢复调度器、vTaskStepTick

    1 开启调度器 br vTaskStartScheduler 43 vPortSetupTimerInterrupt 设置systick xff0c 初始化低功耗运行系统补偿时间 br 43 xPortStartScheduler 43 p
  • 通过Flask框架封装Tushare获取的日线股票数据

    概要介绍 概要介绍 xff08 TuShare id 282782 xff09 当我们需要进行量化交易分析 xff0c 或者通过代码进行股票的数据计算 xff0c 研究金融时 xff0c 我们需要获取最基本的股票价格 xff0c 开盘价收盘
  • linux系统安装硬盘分区建议

    笔者使用linux也很长时间了 xff0c 但总有在使用一段时间之后感觉系统分区不是很合理 xff0c 这里就算是给自己总结一下 xff0c 也跟大家一起分享吧 一 常见挂载点的情况说明 一般来说 xff0c 在linux系统中都有最少两个
  • Python3.4简单爬虫实现之抓取糗事百科段子

    网上的python教程大都是2 X版本的 xff0c python2 X和python3 X相比较改动比较大 xff0c 好多库的用法不太一样 xff0c 我安装的是3 4 1 xff0c 就用3 4 1实现一下网页内容抓取 首先是库 xf
  • 关于stm32中串口重定向问题详解(找个时间好好理解下)

    usart这部分代码我也是从网上copy出来的 xff0c 一下是作者的解释 xff1a 简单地说 xff1a 想在mdk 中用printf xff0c 需要同时重定义fputc函数和避免使用semihosting 半主机模式 xff09
  • http解析库http-parser

    一 http parser简介 1 简介 http parser是一个用C编写的HTTP消息解析器 xff0c 可以解析请求和响应 xff0c 被设计用于高性能HTTP应用程序 它不会进行任何系统调用及内存分配 xff0c 它不会缓冲数据