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;
}