libhv是c++編寫HTTP API 服務端/客戶端最簡單的庫,沒有之一
具有以下特性:
- 跨平臺(
Windows, Linux, Mac
) - 支持
https
- 支持
RESTful API
- 支持
application/json
、application/x-www-form-urlencoded
、multipart/form-data
- 內置
web service
文件服務和indexof service
目錄服務 - 可擴展
多進程/多線程
模型
libhv簡介
libhv是一個跨平臺的類似libevent、libev、libuv
的異步IO事件循環庫,但提供了更加簡單的API接口和更加豐富的協議(包括http、ftp、smtp、dns、icmp
等)。
libhv已廣泛實用在公司的IOT平臺、http API服務之中,正確性、穩定性、可擴展性、性能都有保證,完全開源,請放心使用。
項目地址:https://github.com/ithewei/libhv.git
碼雲鏡像:https://gitee.com/ithewei/libhv.git
QQ技術交流羣:739352073
libhv每日一學博文:https://hewei.blog.csdn.net/article/details/103903123
http服務端最簡版
添加好path=>handler
API映射,設置監聽端口,調用http_server_run
即可
最簡版(僅實現/echo
)
#include "HttpServer.h"
int http_api_echo(HttpRequest* req, HttpResponse* res) {
res->body = req->body;
return 0;
}
int main() {
HttpService service;
service.AddApi("/echo", HTTP_POST, http_api_echo);
http_server_t server;
server.port = 8080;
server.service = &service;
http_server_run(&server);
return 0;
}
g++ -std=c++11 http_server.cpp -o http_server -Iinclude/hv -Llib -lhv
http客戶端最簡版
http_client_send
一個接口即可完成http請求,以調用/echo
接口爲例
#include "http_client.h"
int main(int argc, char* argv[]) {
HttpRequest req;
req.method = HTTP_POST;
req.url = "http://localhost:8080/v1/api/echo";
req.body = "hello,world!";
HttpResponse res;
int ret = http_client_send(&req, &res);
printf("%s\n", req.Dump(true,true).c_str());
if (ret != 0) {
printf("* Failed:%s:%d\n", http_client_strerror(ret), ret);
}
else {
printf("%s\n", res.Dump(true,true).c_str());
}
return ret;
}
g++ -std=c++11 http_client.cpp -o http_client -Iinclude/hv -Llib -lhv
運行結果:
$ ./http_server &
$ ./http_client
POST /v1/api/echo HTTP/1.1
Accept: */*
Content-Length: 12
Content-Type: text/plain
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
hello,world!
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain
Date: Tue, 21 Jan 2020 02:35:58 GMT
Server: httpd/1.20.1.14
hello,world!
http服務端完整版
完整版(演示application/json
、application/x-www-form-urlencoded
、multipart/form-data
、RESTful API
、多進程/多線程
)
#include "HttpServer.h"
// XXX(path, method, handler)
#define HTTP_API_MAP(XXX) \
XXX("/hello", GET, http_api_hello) \
XXX("/query", GET, http_api_query) \
XXX("/echo", POST, http_api_echo) \
XXX("/kv", POST, http_api_kv) \
XXX("/json", POST, http_api_json) \
XXX("/form", POST, http_api_form) \
XXX("/grpc", POST, http_api_grpc) \
\
XXX("/test", POST, http_api_test) \
XXX("/group/:group_name/user/:user_id", DELETE, http_api_restful) \
void response_status(HttpResponse* res, int code, const char* message) {
res->Set("code", code);
res->Set("message", message);
}
int http_api_preprocessor(HttpRequest* req, HttpResponse* res) {
//printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
//printf("%s\n", req->Dump(true, true).c_str());
req->ParseBody();
res->content_type = APPLICATION_JSON;
return 0;
}
int http_api_postprocessor(HttpRequest* req, HttpResponse* res) {
res->DumpBody();
//printf("%s\n", res->Dump(true, true).c_str());
return 0;
}
int http_api_hello(HttpRequest* req, HttpResponse* res) {
res->body = "hello";
return 0;
}
int http_api_query(HttpRequest* req, HttpResponse* res) {
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
// ?query => HttpRequest::query_params
for (auto& param : req->query_params) {
res->Set(param.first.c_str(), param.second);
}
response_status(res, 0, "Query completed.");
return 0;
}
int http_api_echo(HttpRequest* req, HttpResponse* res) {
res->content_type = req->content_type;
res->body = req->body;
return 0;
}
int http_api_kv(HttpRequest*req, HttpResponse* res) {
if (req->content_type != APPLICATION_URLENCODED) {
res->status_code = HTTP_STATUS_BAD_REQUEST;
return 0;
}
res->content_type = APPLICATION_URLENCODED;
res->kv = req->kv;
return 0;
}
int http_api_json(HttpRequest* req, HttpResponse* res) {
if (req->content_type != APPLICATION_JSON) {
res->status_code = HTTP_STATUS_BAD_REQUEST;
return 0;
}
res->content_type = APPLICATION_JSON;
res->json = req->json;
return 0;
}
int http_api_form(HttpRequest* req, HttpResponse* res) {
if (req->content_type != MULTIPART_FORM_DATA) {
res->status_code = HTTP_STATUS_BAD_REQUEST;
return 0;
}
res->content_type = MULTIPART_FORM_DATA;
res->form = req->form;
return 0;
}
int http_api_grpc(HttpRequest* req, HttpResponse* res) {
if (req->content_type != APPLICATION_GRPC) {
res->status_code = HTTP_STATUS_BAD_REQUEST;
return 0;
}
// parse protobuf: ParseFromString
// req->body;
// res->content_type = APPLICATION_GRPC;
// serailize protobuf: SerializeAsString
// res->body;
return 0;
}
int http_api_test(HttpRequest* req, HttpResponse* res) {
string str = req->GetString("string");
//int64_t n = req->Get<int64_t>("int");
//double f = req->Get<double>("float");
//bool b = req->Get<bool>("bool");
int64_t n = req->GetInt("int");
double f = req->GetFloat("float");
bool b = req->GetBool("bool");
res->content_type = req->content_type;
res->Set("string", str);
res->Set("int", n);
res->Set("float", f);
res->Set("bool", b);
response_status(res, 0, "OK");
return 0;
}
int http_api_restful(HttpRequest* req, HttpResponse* res) {
// RESTful /:field/ => HttpRequest::query_params
// path=/group/:group_name/user/:user_id
//string group_name = req->GetParam("group_name");
//string user_id = req->GetParam("user_id");
for (auto& param : req->query_params) {
res->Set(param.first.c_str(), param.second);
}
response_status(res, 0, "Operation completed.");
return 0;
}
int main() {
HttpService service;
service.base_url = "/v1/api";
service.preprocessor = http_api_preprocessor;
service.postprocessor = http_api_postprocessor;
//service.AddApi("/hello", HTTP_GET, http_api_hello);
#define XXX(path, method, handler) \
service.AddApi(path, HTTP_##method, handler);
HTTP_API_MAP(XXX)
#undef XXX
http_server_t server;
server.port = 8080;
server.worker_processes = 4;
server.service = &service;
http_server_run(&server);
return 0;
}
編譯測試
git clone https://github.com/ithewei/libhv.git
cd libhv
make httpd curl
bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd
# http web service
bin/curl -v localhost:8080
# http indexof service
bin/curl -v localhost:8080/downloads/
# http api service
bin/curl -v localhost:8080/v1/api/hello
bin/curl -v localhost:8080/v1/api/echo -d "hello,world!"
bin/curl -v localhost:8080/v1/api/query?page_no=1\&page_size=10
bin/curl -v localhost:8080/v1/api/kv -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
bin/curl -v localhost:8080/v1/api/test -F 'bool=1 int=123 float=3.14 string=hello'
# RESTful API: /group/:group_name/user/:user_id
bin/curl -v -X DELETE localhost:8080/v1/api/group/test/user/123
# webbench (linux only)
make webbench
bin/webbench -c 2 -t 60 localhost:8080