使用nodejs中的llhttp库实现http解码

68 篇文章 3 订阅
21 篇文章 1 订阅

http协议解码:https://github.com/nodejs/llhttp

url字段解析:https://github.com/netmindms/urlparser

url中的解码:https://github.com/int2e/UrlEncoder

关于URL编码:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

llhttp库号称是httpparser的升级版,解析http协议更加快速,

在某些时候我们需要自己实现一个嵌入式的微型HTTP服务,需要对HTTP数据进行解码:

这个库网上找不到什么说明和示例,但是发现使用并不是很难,主要是设置几个回调函数,执行解析时候根据回调函数来处理数据。回调和异步是贯穿所有nodejs项目的一个特色,示例代码如下:

// testIIHttp.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <string>
#include <iostream>
#include "../llhttp/llhttp.h"
#include "../llhttp/EdUrlParser.h"
#include "../llhttp/Encoder.h"
#include "Timer.h"

using namespace  std;
using namespace robin;

#define  PRINT(...) printf(__VA_ARGS__)
//#define  PRINT(...) 

int onHeaderField(llhttp_t *parser, const char *at, size_t length)
{
	string str = std::string(at, length);
	PRINT("field=%s\n", str.c_str());
	return 0;
}

int onHeaderValue(llhttp_t *parser, const char *at, size_t length)
{
	string str = std::string(at, length);
	PRINT("value=%s\n", str.c_str());

	return 0;
}


void parseUrl(string & s)
{
	//string temp = EdUrlParser::urlDecode(s);
	//cout << temp << endl;
	EdUrlParser* url = EdUrlParser::parseUrl(s);
	/*cout << "scheme: " + url->scheme << endl;
	cout << "host name: " + url->hostName << endl;
	cout << "port: " + url->port << endl;
	cout << "path: " + url->path << endl;
	cout << "param: " + url->query << endl;
	cout << "fragment: " + url->fragment << endl;*/

	// parse path
	vector<string> paths;
	EdUrlParser::parsePath(&paths, url->path);
	for (int i = 0; i < paths.size(); i++) {
		//cout << "path part: " + paths[i] << endl;
		PRINT("path part: %d", paths[i].c_str());
	}

	// parse query string as key-value list style
	//vector<query_kv_t> kvs;
	//int num = EdUrlParser::parseKeyValueList(&kvs, url->query);
	//for (int i = 0; i < num; i++) {
	//	PRINT("idx:%d  key: %s, val: %s\n", i, kvs[i].key.c_str(), kvs[i].val.c_str());
	//}

	// parse query string as key-value hash map
	unordered_map<string, string> map;
	int mapnum = EdUrlParser::parseKeyValueMap(&map, url->query);

	Encoder encoder;

	string name;
	try {
		name = map.at("wd");
		string temp = encoder.UTF8UrlDecode(name);
		PRINT("key wd = '%s'\n", temp.c_str());
	}
	catch (out_of_range err) {
		PRINT("### Error: not found...\n");
	};


	if (url != NULL) // ==> make sure to free url object allocated by EdUrlParser
		delete url;

	// test url encoding
	//string enc = EdUrlParser::urlEncode("left| |right & ");
	//cout << "encoded string: " << enc << endl;
	//string dec = EdUrlParser::urlDecode(enc);
	//cout << "decoded string: " << dec << endl;
}

void attach(llhttp_settings_t *settings)
{
	settings->on_message_begin = nullptr;
	settings->on_status = nullptr;
	settings->on_chunk_header = nullptr;
	settings->on_chunk_complete = nullptr;

	settings->on_url = [](llhttp_t *parser, const char *at, size_t length) -> int
	{
		switch (parser->method)
		{
		case llhttp_method::HTTP_GET :
			PRINT("GET ");
			break;
		case llhttp_method::HTTP_POST:
			PRINT("POST ");
			break;
		default:
			PRINT("method=%d ", parser->method);
		}
		string str = std::string(at, length);
		PRINT("url=%s\n", str.c_str());
		parseUrl(str);

		PRINT("\n");

		return 0;
	};

	settings->on_header_field = onHeaderField;
	settings->on_header_value = onHeaderValue;

	settings->on_headers_complete = [](llhttp_t *parser) -> int {

		int status = parser->status_code;
		

		if (parser->type == HTTP_REQUEST) 
		{
			int method = parser->method;
		}


		return 0;
	};

	settings->on_body = [](llhttp_t *parser, const char *at, size_t len) -> int
	{
		string str = string(at, len);
		PRINT("body=%s\n", str.c_str());

		return 0;
	};

	settings->on_message_complete = [](llhttp_t *parser) -> int
	{
		// 
		PRINT("begin to call worker\n");

		return 0;
	};
}


int main()
{
	llhttp_t parser;
	llhttp_settings_t settings;

	/* Initialize user callbacks and settings */
	llhttp_settings_init(&settings);

	/* Set user callback */
	//settings.on_message_complete = handle_on_message_complete;
	attach(&settings);

	/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
	 * HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
	 * input.
	 */
	llhttp_init(&parser, HTTP_BOTH, &settings);

	/* Parse request! */
	std::string str =   // "GET / HTTP/1.1\r\n\r\n";
		//"GET /test.json HTTP/1.1\r\n"
		"GET https://www.baidu.com/s?wd=%E4%BF%AE%E6%AD%A3%E8%8D%AF%E4%B8%9A&rsv_spt=1&rsv_iqid=0xe82cb2af00013906&issp=1&f=3&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=&tn=baiduhome_pg&ch=&rsv_dl=ib&rsv_enter=1&rsv_btype=i HTTP/1.1\r\n"
		"Host:www.weather.com.cn\r\n"
		"User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0\r\n"
		"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n"
		"\r\n";
	const char* request = str.c_str();
	int len = str.size();

	Timer timer;
	timer.start();
	enum llhttp_errno err = llhttp_execute(&parser, request, len);
	if (err == HPE_OK)
	{
		/* Successfully parsed! */
	}
	else
	{
		fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
			parser.reason);
	}
	double delta = timer.stop_delta<Timer::ms>();
	cout << delta << endl;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值