c++編寫HTTP API服務端/客戶端最簡單的庫,沒有之一

libhv是c++編寫HTTP API 服務端/客戶端最簡單的庫,沒有之一

具有以下特性:

  • 跨平臺(Windows, Linux, Mac)
  • 支持https
  • 支持RESTful API
  • 支持application/jsonapplication/x-www-form-urlencodedmultipart/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=>handlerAPI映射,設置監聽端口,調用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/jsonapplication/x-www-form-urlencodedmultipart/form-dataRESTful 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
發佈了128 篇原創文章 · 獲贊 142 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章