libhv是c++编写HTTP API 服务端/客户端最简单的库,没有之一

具有以下特性:

  • 跨平台(Windows, Linux, Mac)
  • 支持https
  • 支持application/jsonapplication/x-www-form-urlencodedmultipart/form-data
  • 支持静态文件服务、目录服务、同步/异步API处理函数
  • 支持RESTful风格、URI路由、keep-alive长连接、chunked分块等特性
  • http服务端类似golanggin
  • http客户端类似pythonrequests
  • 可扩展多进程/多线程模型

libhv简介

libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。

项目地址:https://github.com/ithewei/libhv.git
码云镜像:https://gitee.com/libhv/libhv.git
QQ技术交流群:739352073
libhv博客专栏:https://hewei.blog.csdn.net/category_9866493.html
libhv源码分析:https://blog.csdn.net/qu1993/category_10637982.html

http服务端最简版

examples/http_server_test
添加好path=>handlerAPI路由,设置监听端口,调用http_server_run即可

示例代码参考examples/http_server_test.cpp

#include "hv/HttpServer.h"

int main() {
    HttpService router;

    // curl -v http://ip:port/
    router.Static("/", "./html");

    // curl -v http://ip:port/proxy/get
    router.Proxy("/proxy/", "http://httpbin.org/");

    // curl -v http://ip:port/ping
    router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
        return resp->String("pong");
    });

    // curl -v http://ip:port/data
    router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
        static char data[] = "0123456789";
        return resp->Data(data, 10 /*, false */);
    });

    // curl -v http://ip:port/paths
    router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
        return resp->Json(router.Paths());
    });

    // curl -v http://ip:port/get?env=1
    router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
        resp->json["origin"] = req->client_addr.ip;
        resp->json["url"] = req->url;
        resp->json["args"] = req->query_params;
        resp->json["headers"] = req->headers;
        return 200;
    });

    // curl -v http://ip:port/echo -d "hello,world!"
    router.POST("/echo", [](const HttpContextPtr& ctx) {
        return ctx->send(ctx->body(), ctx->type());
    });

    // curl -v http://ip:port/user/123
    router.GET("/user/{id}", [](const HttpContextPtr& ctx) {
        hv::Json resp;
        resp["id"] = ctx->param("id");
        return ctx->send(resp.dump(2));
    });

    http_server_t server;
    server.port = 8080;
    server.service = &router;
    http_server_run(&server);
    return 0;
}

友情提示:
上面示例直接运行在main主线程,http_server_run(&server, 1);会阻塞当前线程运行,所以routerserver对象不会被析构,如使用http_server_run(&server, 0);内部会另起线程运行,不会阻塞当前线程,此时需要注意routerserver的生命周期,不要定义为局部变量被析构了,需定义为类成员变量或者全局变量

编译运行:

c++ -std=c++11 examples/http_server_test.cpp -o bin/http_server_test -lhv
bin/http_server_test

测试使用curl或浏览器输入以下url

curl -v http://127.0.0.1:8080/ping
curl -v http://127.0.0.1:8080/data
curl -v http://127.0.0.1:8080/paths
curl -v http://127.0.0.1:8080/echo -d "hello,world"
curl -v http://127.0.0.1:8080/user/123

http客户端最简版

examples/http_client_test
用法类似pythonrequestsrequests::request一个接口即可完成请求响应

#include "requests.h"

int main() {
    auto resp = requests::get("http://www.example.com");
    if (resp == NULL) {
        printf("request failed!\n");
    } else {
        printf("%d %s\r\n", resp->status_code, resp->status_message());
        printf("%s\n", resp->body.c_str());
    }

    auto resp2 = requests::post("127.0.0.1:8080/echo", "hello,world!");
    if (resp2 == NULL) {
        printf("request failed!\n");
    } else {
        printf("%d %s\r\n", resp2->status_code, resp2->status_message());
        printf("%s\n", resp2->body.c_str());
    }

    return 0;
}
g++ -std=c++11 http_client_test.cpp -o http_client_test -Iinclude/hv -Llib -lhv

http服务端完整版

examples/httpd

class Router {
public:
    static void Register(HttpService& router) {
        // preprocessor => Handler => postprocessor
        router.preprocessor = Handler::preprocessor;
        router.postprocessor = Handler::postprocessor;
        router.largeFileHandler = Handler::largeFileHandler;
        // router.errorHandler = Handler::errorHandler;

        // curl -v http://ip:port/ping
        router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
            return resp->String("pong");
        });

        // curl -v http://ip:port/data
        router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
            static char data[] = "0123456789";
            return resp->Data(data, 10 /*, false */);
        });

        // curl -v http://ip:port/html/index.html
        router.GET("/html/index.html", [](HttpRequest* req, HttpResponse* resp) {
            return resp->File("html/index.html");
        });

        // curl -v http://ip:port/paths
        router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
            return resp->Json(router.Paths());
        });

        // curl -v http://ip:port/get?env=1
        router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
            resp->json["origin"] = req->client_addr.ip;
            resp->json["url"] = req->url;
            resp->json["args"] = req->query_params;
            resp->json["headers"] = req->headers;
            return 200;
        });

        // curl -v http://ip:port/service
        router.GET("/service", [](const HttpContextPtr& ctx) {
            ctx->setContentType("application/json");
            ctx->set("base_url", ctx->service->base_url);
            ctx->set("document_root", ctx->service->document_root);
            ctx->set("home_page", ctx->service->home_page);
            ctx->set("error_page", ctx->service->error_page);
            ctx->set("index_of", ctx->service->index_of);
            return 200;
        });

        // curl -v http://ip:port/echo -d "hello,world!"
        router.POST("/echo", [](const HttpContextPtr& ctx) {
            return ctx->send(ctx->body(), ctx->type());
        });

        // wildcard *
        // curl -v http://ip:port/wildcard/any
        router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) {
            std::string str = req->path + " match /wildcard*";
            return resp->String(str);
        });

        // curl -v http://ip:port/async
        router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
            writer->WriteHeader("X-Request-tid", hv_gettid());
            std::async([req, writer](){
                writer->WriteHeader("X-Response-tid", hv_gettid());
                writer->WriteHeader("Content-Type", "text/plain");
                writer->WriteBody("This is an async response.\n");
                writer->End();
            });
        });

        // curl -v http://ip:port/www.*
        // curl -v http://ip:port/www.example.com
        router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
            HttpRequestPtr req2(new HttpRequest);
            req2->url = req->path.substr(1);
            requests::async(req2, [writer](const HttpResponsePtr& resp2){
                writer->Begin();
                if (resp2 == NULL) {
                    writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
                    writer->WriteHeader("Content-Type", "text/html");
                    writer->WriteBody("<center><h1>404 Not Found</h1></center>");
                } else {
                    writer->WriteResponse(resp2.get());
                }
                writer->End();
            });
        });

        // curl -v http://ip:port/sleep?t=1000
        router.GET("/sleep", Handler::sleep);

        // curl -v http://ip:port/setTimeout?t=1000
        router.GET("/setTimeout", Handler::setTimeout);

        // curl -v http://ip:port/query?page_no=1\&page_size=10
        router.GET("/query", Handler::query);

        // Content-Type: application/x-www-form-urlencoded
        // curl -v http://ip:port/kv -H "content-type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
        router.POST("/kv", Handler::kv);

        // Content-Type: application/json
        // curl -v http://ip:port/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
        router.POST("/json", Handler::json);

        // Content-Type: multipart/form-data
        // bin/curl -v localhost:8080/form -F "user=admin pswd=123456"
        router.POST("/form", Handler::form);

        // curl -v http://ip:port/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
        // curl -v http://ip:port/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
        // bin/curl -v http://ip:port/test -F 'bool=1 int=123 float=3.14 string=hello'
        router.POST("/test", Handler::test);

        // Content-Type: application/grpc
        // bin/curl -v --http2 http://ip:port/grpc -H "content-type:application/grpc" -d 'protobuf'
        router.POST("/grpc", Handler::grpc);

        // RESTful API: /group/:group_name/user/:user_id
        // curl -v -X DELETE http://ip:port/group/test/user/123
        router.Delete("/group/:group_name/user/:user_id", Handler::restful);
        // router.Delete("/group/{group_name}/user/{user_id}", Handler::restful);

        // bin/curl -v localhost:8080/upload -F "file=@LICENSE"
        router.POST("/upload", Handler::upload);

        // curl -v http://ip:port/login -H "Content-Type:application/json" -d '{"username":"admin","password":"123456"}'
        router.POST("/login", Handler::login);
    }
};

完整版演示了application/jsonapplication/x-www-form-urlencodedmultipart/form-dataRESTful API多进程/多线程

赶快git clone https://github.com/ithewei/libhv.git体验吧

git clone https://github.com/ithewei/libhv.git
cd libhv
make

bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd

# http file service
bin/curl -v localhost:8080

# http indexof service
bin/curl -v localhost:8080/downloads/

# http api service
bin/curl -v localhost:8080/ping
bin/curl -v localhost:8080/echo -d "hello,world!"
bin/curl -v localhost:8080/query?page_no=1\&page_size=10
bin/curl -v localhost:8080/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
bin/curl -v localhost:8080/form -F "user=admin pswd=123456"
bin/curl -v localhost:8080/upload -F "file=@LICENSE"

bin/curl -v localhost:8080/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
bin/curl -v localhost:8080/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
bin/curl -v localhost:8080/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/group/test/user/123
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐