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 篇原创文章 · 获赞 140 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/GG_SiMiDa/article/details/104055509