HTTP解析库http-parser简介及使用

2023-05-16

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

特性:不依赖第三方库;处理持续流(keep-alive);分块解码(decodes chunked encoding);支持Upgrade;防止缓冲区溢出攻击。

可以从HTTP消息中解析下列信息:报头域及值(Header fields and values);内容长度(Content-Length);请求方法;响应状态码;传输编码;HTTP版本;请求URL(网址);消息体(Message body)。

每个TCP连接使用一个http_parser对象。使用http_parser_init函数初始化结构体并设置回调。

下面的测试代码是修改于http-parser中的test.c文件:

#include "funset.hpp"
#include <string.h>
#include <assert.h>
#include <iostream>
#include <http-parser/http_parser.h>

namespace {

#define MAX_HEADERS 13
#define MAX_ELEMENT_SIZE 2048
#define MAX_CHUNKS 16

struct message {
	const char *name; // for debugging purposes
	const char *raw;
	enum http_parser_type type;
	enum http_method method;
	int status_code;
	char response_status[MAX_ELEMENT_SIZE];
	char request_path[MAX_ELEMENT_SIZE];
	char request_url[MAX_ELEMENT_SIZE];
	char fragment[MAX_ELEMENT_SIZE];
	char query_string[MAX_ELEMENT_SIZE];
	char body[MAX_ELEMENT_SIZE];
	size_t body_size;
	const char *host;
	const char *userinfo;
	uint16_t port;
	int num_headers;
	enum { NONE = 0, FIELD, VALUE } last_header_element;
	char headers[MAX_HEADERS][2][MAX_ELEMENT_SIZE];
	int should_keep_alive;

	int num_chunks;
	int num_chunks_complete;
	int chunk_lengths[MAX_CHUNKS];

	const char *upgrade; // upgraded body

	unsigned short http_major;
	unsigned short http_minor;

	int message_begin_cb_called;
	int headers_complete_cb_called;
	int message_complete_cb_called;
	int status_cb_called;
	int message_complete_on_eof;
	int body_is_final;
};

int num_messages = 0;
http_parser* parser = nullptr;
struct message messages[5];
int currently_parsing_eof = 0;

int message_begin_cb(http_parser* p)
{
	assert(p == parser);
	messages[num_messages].message_begin_cb_called = true;
	return 0;
}

size_t strnlen(const char* s, size_t maxlen)
{
	const char* p = (const char*)memchr(s, '\0', maxlen);
	if (p == NULL)
		return maxlen;

	return p - s;
}

size_t strlncat(char* dst, size_t len, const char* src, size_t n)
{
	size_t slen = strnlen(src, n);
	size_t dlen = strnlen(dst, len);

	if (dlen < len) {
		size_t rlen = len - dlen;
		size_t ncpy = slen < rlen ? slen : (rlen - 1);
		memcpy(dst + dlen, src, ncpy);
		dst[dlen + ncpy] = '\0';
	}

	assert(len > slen + dlen);
	return slen + dlen;
}

int header_field_cb(http_parser* p, const char* buf, size_t len)
{
	assert(p == parser);
	struct message *m = &messages[num_messages];

	if (m->last_header_element != m->FIELD)
		m->num_headers++;

	strlncat(m->headers[m->num_headers - 1][0], sizeof(m->headers[m->num_headers - 1][0]), buf, len);
	m->last_header_element = m->FIELD;
	return 0;
}

int header_value_cb(http_parser* p, const char* buf, size_t len)
{
	assert(p == parser);
	message *m = &messages[num_messages];

	strlncat(m->headers[m->num_headers - 1][1], sizeof(m->headers[m->num_headers - 1][1]), buf, len);
	m->last_header_element = m->VALUE;
	return 0;
}

int request_url_cb(http_parser* p, const char* buf, size_t len)
{
	assert(p == parser);
	strlncat(messages[num_messages].request_url, sizeof(messages[num_messages].request_url), buf, len);
	return 0;
}

int response_status_cb(http_parser*p, const char* buf, size_t len)
{
	assert(p == parser);
	messages[num_messages].status_cb_called = true;

	strlncat(messages[num_messages].response_status, sizeof(messages[num_messages].response_status), buf, len);
	return 0;
}

void check_body_is_final(const http_parser* p)
{
	if (messages[num_messages].body_is_final) {
		fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
			"on last on_body callback call "
			"but it doesn't! ***\n\n");
		assert(0);
		abort();
	}
	messages[num_messages].body_is_final = http_body_is_final(p);
}

int body_cb(http_parser* p, const char* buf, size_t len)
{
	assert(p == parser);
	strlncat(messages[num_messages].body, sizeof(messages[num_messages].body), buf, len);
	messages[num_messages].body_size += len;
	check_body_is_final(p);
	return 0;
}

int headers_complete_cb(http_parser* p)
{
	assert(p == parser);
	messages[num_messages].method = (http_method)parser->method;
	messages[num_messages].status_code = parser->status_code;
	messages[num_messages].http_major = parser->http_major;
	messages[num_messages].http_minor = parser->http_minor;
	messages[num_messages].headers_complete_cb_called = true;
	messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
	return 0;
}

int message_complete_cb(http_parser* p)
{
	assert(p == parser);
	if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser)) {
		fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
			"value in both on_message_complete and on_headers_complete "
			"but it doesn't! ***\n\n");
		assert(0);
		abort();
	}

	if (messages[num_messages].body_size && http_body_is_final(p) && !messages[num_messages].body_is_final) {
		fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
			"on last on_body callback call "
			"but it doesn't! ***\n\n");
		assert(0);
		abort();
	}

	messages[num_messages].message_complete_cb_called = true;
	messages[num_messages].message_complete_on_eof = currently_parsing_eof;
	num_messages++;
	return 0;
}

int chunk_header_cb(http_parser* p)
{
	assert(p == parser);
	int chunk_idx = messages[num_messages].num_chunks;
	messages[num_messages].num_chunks++;
	if (chunk_idx < MAX_CHUNKS) {
		messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;
	}

	return 0;
}

int chunk_complete_cb(http_parser* p)
{
	assert(p == parser);

	/* Here we want to verify that each chunk_header_cb is matched by a
	* chunk_complete_cb, so not only should the total number of calls to
	* both callbacks be the same, but they also should be interleaved
	* properly */
	assert(messages[num_messages].num_chunks == messages[num_messages].num_chunks_complete + 1);

	messages[num_messages].num_chunks_complete++;
	return 0;
}

http_parser_settings settings_null = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
http_parser_settings settings = { message_begin_cb, request_url_cb, response_status_cb, header_field_cb, header_value_cb,
				headers_complete_cb, body_cb, message_complete_cb, chunk_header_cb, chunk_complete_cb };
http_parser_settings settings_count_body = {};/*{ message_begin_cb, request_url_cb, response_status_cb, header_field_cb, header_value_cb,
				headers_complete_cb, count_body_cb, message_complete_cb, chunk_header_cb, chunk_complete_cb};*/
http_parser_settings settings_pause = {};/* { pause_message_begin_cb, pause_request_url_cb, pause_response_status_cb, pause_header_field_cb, pause_header_value_cb
				pause_headers_complete_cb, pause_body_cb, pause_message_complete_cb, pause_chunk_header_cb, pause_chunk_complete_cb};*/

int test_no_overflow_long_body(int req, size_t length)
{
	http_parser parser;
	http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
	char buf1[3000];
	size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
		req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
	size_t parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
	if (parsed != buf1len) return -1;

	for (size_t i = 0; i < length; i++) {
		char foo = 'a';
		parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
		if (parsed != 1) return -1;
	}

	parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
	if (parsed != buf1len) return -1;

	return 0;
}

void parser_init(http_parser_type type)
{
	num_messages = 0;
	assert(parser == NULL);
	parser = (http_parser*)malloc(sizeof(http_parser));
	http_parser_init(parser, type);
	memset(&messages, 0, sizeof messages);
}

void parser_free()
{
	assert(parser);
	free(parser);
	parser = NULL;
}

size_t parse(const char* buf, size_t len)
{
	size_t nparsed;
	currently_parsing_eof = (len == 0);
	nparsed = http_parser_execute(parser, &settings, buf, len);
	return nparsed;
}

//size_t parse_count_body(const char *buf, size_t len)
//{
//	size_t nparsed;
//	currently_parsing_eof = (len == 0);
//	nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
//	return nparsed;
//}

//size_t parse_pause(const char *buf, size_t len)
//{
//	size_t nparsed;
//	http_parser_settings s = settings_pause;
//
//	currently_parsing_eof = (len == 0);
//	current_pause_parser = &s;
//	nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
//	return nparsed;
//}

//size_t parse_connect(const char *buf, size_t len)
//{
//	size_t nparsed;
//	currently_parsing_eof = (len == 0);
//	nparsed = http_parser_execute(parser, &settings_connect, buf, len);
//	return nparsed;
//}

int test_simple_type(const char* buf, http_errno err_expected, http_parser_type type)
{
	parser_init(type);
	parse(buf, strlen(buf));
	http_errno err = HTTP_PARSER_ERRNO(parser);
	parse(NULL, 0);
	parser_free();

	// In strict mode, allow us to pass with an unexpected HPE_STRICT as long as the caller isn't expecting success.
	if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
		fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n", http_errno_name(err_expected), http_errno_name(err), buf);
		return -1;
	}

	return 0;
}

} // namespace

int test_http_parser()
{
	// reference: http-parser/test.c
{ // get http-parser version
	unsigned long version = http_parser_version();
	unsigned int major = (version >> 16) & 255;
	unsigned int minor = (version >> 8) & 255;
	unsigned int patch = version & 255;
	fprintf(stdout, "http_parser version: %u.%u.%u\n", major, minor, patch);
}

{ // test preserve data
	char my_data[] = "application-specific data";
	http_parser parser;
	parser.data = my_data;
	http_parser_init(&parser, HTTP_REQUEST);
	if (parser.data != my_data) {
		fprintf(stderr, "\n*** parser.data not preserved accross http_parser_init ***\n\n");
		return -1;
	}
}

{ // test method str
	const char* str1 = http_method_str(HTTP_GET);
	const char* str2 = http_method_str((http_method)1337);
	fprintf(stdout, "http method: str1: %s, str2: %s\n", str1, str2);
}

{ // test header nread value
	http_parser parser;
	http_parser_init(&parser, HTTP_REQUEST);
	const char* buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
	size_t len = strlen(buf);
	size_t parsed = http_parser_execute(&parser, &settings_null, buf, len);
	if (parsed != len || parser.nread != len) {
		fprintf(stderr, "fail to http_parser_execute: parsed: %d, len: %d, parser.nread: %d\n", parsed, len, parser.nread);
		return -1;
	}
}

{ // test no overflow parse url
	http_parser_url u;
	http_parser_url_init(&u);
	int rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u);
	if (rv != 0 || u.port != 800) {
		fprintf(stderr, "return value: %d, prot number: %d\n", rv, u.port);
		return -1;
	}
}

{ // test no overflow long body
	if (test_no_overflow_long_body(HTTP_REQUEST, 1000) || test_no_overflow_long_body(HTTP_REQUEST, 100000) ||
		test_no_overflow_long_body(HTTP_RESPONSE, 1000) || test_no_overflow_long_body(HTTP_RESPONSE, 100000)) {
		fprintf(stderr, "fail to test no overflow long body\n");
		return -1;
	}
}

{ // test simple type
	if (test_simple_type(
		"POST / HTTP/1.1\r\n"
		"Content-Length:  42 \r\n"  // Note the surrounding whitespace.
		"\r\n",
		HPE_OK,
		HTTP_REQUEST)) {
		fprintf(stderr, "fail to test simple type\n");
		return -1;
	}
}

	return 0;
}

执行结果如下:

GitHub: https://github.com/fengbingchun/OpenSSL_Test 

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

HTTP解析库http-parser简介及使用 的相关文章

  • 电解电容的ESR,想说三句话

    电容的ESR是指电容的等效串联电阻 xff08 或阻抗 xff09 理想的电容 xff0c 是没有电阻的 但是实际上 xff0c 任何电容都有电阻 xff0c 这个电阻值和电容的材料 结构有关系 1 那些 贴片电容 选用贴片电容的时候 xf
  • Matlab2012b&Simulink licence失效解决办法(重复激活解决方案)

    光棍节结束 xff0c math公司也对用户端进行调整 xff0c 很多朋友的matlab都被要求重新添加许可文件 然而 xff0c 基本都是激活完成 xff0c 打开 xff0c 继续激活 xff0c 然后激活完成重复 这是因为激活的文件
  • 航模飞机飞行力效和飞行时间的算法

    力效 xff08 g w xff09 总起飞重量 xff08 g 除起飞功率 xff08 w xff09 例 xff1a 有一架飞机 xff0c 总起飞重量是 8KG xff0c 也就是 8000G xff0c 悬停电流是 40A xff0
  • 5脚继电器的接法

    5脚继电器原理图和接法 一般情况 xff0c 三只脚的那一边中间脚是输出触点的公共端子 xff0c 另外两个引脚是线圈 xff0c 即接驱动端 另外2个脚那边分别是常开和常闭触点 如下图 xff1a A B 脚接驱动电路端 要控制的电路接1
  • Python -- argparse :命令行参数解析模块

    Python argparse xff1a 命令行参数解析模块 官网参考文档 文章目录 Python argparse xff1a 命令行参数解析模块1 总述2 96 add argument 96 2 1 name or flags2 2
  • PWM波控制舵机总结

    文章转载自 https www cnblogs com zhoubatuo p 6138033 html 一 关于舵机 xff1a 舵机 xff08 英文叫Servo xff09 xff1a 它由直流电机 减速齿轮组 传感器和控制电路组成的
  • PNP三极管和NPN三极管的开关电路

    一 三极管开关电路设计的可行性及必要性 可行性 xff1a 用过三极管的人都清楚 xff0c 三极管有一个特性 xff0c 就是有饱和状态与截止状态 xff0c 正是因为有了这两种状态 xff0c 使其应用于开关电路成为可能 必要性 xff
  • Clark变换与Park(派克)变换

    转载https blog csdn net chenjianbo88 article details 53027298 clark变换 xff1a 将abc 变换到 静止 的 坐标系下 Park变换 xff1a 将abc 变换到 旋转 的
  • 无感方波和FOC堵转检测策略参考

    http mcu eetrend com content 2017 100007230 html 基于S12ZVM的车用无传感器BLDC堵转检测方法探讨 judy 发布于 xff1a 周一 07 31 2017 11 05 xff0c 关键
  • kubernetes 快速入门

    文章目录 2 kubernetes 快速入门前言一 nameSpace1 简介2 常用命令查看 nameSpace创建 nameSpace删除 nameSpace 二 pod 与 deployment1 简介2 常用命令查看 pod创建 d
  • 事件处理的本质

    当在点击一个按钮执行某个操作时 xff0c 你有没有想过 xff0c 为什么点击了这个按钮就会执行某个操作 xff0c 这是为什么 xff1f 那么接下来就让我来解开这里面的秘密 用微软中定义的事件函数来说明这个问题是再简单不过的的事情了
  • 在Ubuntu系统中使用dd工具备份Jetson Xavier NX TF/SD卡

    Jetson Xavier NX TF SD卡系统镜像的备份与恢复 备份环境系统备份系统恢复 本次操作是通过 dd 命令完整克隆系统 这种方法是块设备的 bit 复制 xff0c 所以完全不需要了解上层文件系统的结构和内容 xff0c 只需
  • 深度相机使用对比:Gemini Pro与RealSense D435i

    文章目录 前言一 参数对比二 环境配置三 实际使用效果图像读取视觉slam效果对比 总结 前言 为了开展视觉slam相关项目 xff0c 最近找了几款不同的深度相机进行测试 xff0c 本次主要讲一下奥比中光的Gemini Pro相机与In
  • C++ STL——迭代器

    迭代器 无论是序列容器还是关联容器 xff0c 最常做的操作无疑是遍历容器中存储的元素 xff0c 而实现此操作 xff0c 多数情况会选用 迭代器 xff08 iterator xff09 来实现 我们知道 xff0c 尽管不同容器的内部
  • 理解大端(网络)字节序和小端(部分主机)字节序的区分和转换

    一 基本概念 xff1a 举例 xff1a Mac地址为 xff08 0x xff09 xff1a 00 0c 29 74 33 55 大端字节序就是 xff1a 00 0c 29 74 33 55 xff0c 和我们正常的读写习惯一致 x
  • [图像处理]-Opencv中数据类型CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F是什么?

    1 宏定义 首先来说CV 8U CV 16U CV 16S CV 32F 以及 CV 64F xff0c 都是opencv定义的数据类型 具体定义如下 define CV 8S 1 define CV 16U 2 define CV 16S
  • 基于卡尔曼滤波和PID调节的自平衡小车

    资源下载 xff1a 源代码 xff1a http download csdn net download feng3121 10262828 功能图与程序框图 xff1a http download csdn net download fe
  • SBUS调试助手 sbus解析,sbus协议

    最近在做一个无人机用的四路的开关 即航模遥控器PWM信号控制四路继电器 设计硬件的时候专门用了一路可以配置成串口输入的管脚 外部加了一路施密特反相器 其实主要是滤波防止信号抖动 当然私心就是后续可以接SBUS信号 接触航模遥控器时间挺长了
  • Android WebView 网页使用本地字体

    要求在网页里面调用android app中assets目录下的某个字体文件 网页加载通常有两种方式 xff1a 1 loadDataWithBaseURL 2 loadUrl 一 loadDataWithBaseURL 网页中直接使用fil
  • 若依前后端分离版,图片上传后无法显示问题

    若依前后端分离版 xff0c 部署时通常会采用Nginx做反向代理 访问图片出现404 Not Found问题 若依在文件上传成功后 xff0c 请求访问后台地址会根据profile进行匹配 xff0c 需要自己配置代理 配置方式一 xff

随机推荐