0
点赞
收藏
分享

微信扫一扫

2022-02-17 快速搭建c++的web服务器


目录

​​摘要:​​

​​依赖的库:​​

​​cpp-httplib: ​​

​​备份地址:​​

​​编写c++的web服务器:​​

​​http模块:​​

​​server模块:​​

​​router模块:​​

​​编译:​​

​​makefile:​​

​​build.sh​​

​​功能测试:​​

摘要:

说明如何快速搭建c++的web服务器, 便于后续使用c++编写web业务

总文件在

github:

​​GitHub - adofsauron/cpp-web-mini​​

依赖的库:

cpp-httplib: 


备份地址:

编写c++的web服务器:

目录结构:

.
├── agent
├── build.sh
├── Makefile
├── src
│ ├── base
│ ├── config
│ ├── http
│ │ └── httplib.h
│ ├── proc
│ ├── router
│ │ ├── router.cc
│ │ ├── router.h
│ │ ├── router_test.cc
│ │ └── router_test.h
│ └── server
│ ├── run.cc
│ └── server.h
└── utest
└── ut_testData.sh

8 directories, 11 files

http模块:

//

server模块:

server.h

#include <chrono>
#include <cstdio>
#include "../http/httplib.h"

std::string dump_headers(const httplib::Headers &headers) {
std::string s;
char buf[BUFSIZ] = {0};

for (auto it = headers.begin(); it != headers.end(); ++it) {
const auto &x = *it;
snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
s += buf;
}

return s;
}

std::string log(const httplib::Request &req, const httplib::Response &res) {
std::string s;
char buf[BUFSIZ] = {0};

s += "================================\n";

snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
req.version.c_str(), req.path.c_str());
s += buf;

std::string query;
for (auto it = req.params.begin(); it != req.params.end(); ++it) {
const auto &x = *it;
snprintf(buf, sizeof(buf), "%c%s=%s",
(it == req.params.begin()) ? '?' : '&', x.first.c_str(),
x.second.c_str());
query += buf;
}
snprintf(buf, sizeof(buf), "%s\n", query.c_str());
s += buf;

s += dump_headers(req.headers);

s += "--------------------------------\n";

snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
s += buf;
s += dump_headers(res.headers);
s += "\n";

if (!res.body.empty()) { s += res.body; }

s += "\n";

return s;
}

run.cc



#include "server.h"
#include "../router/router.h"
#include "../router/router_test.h"

#define SERVER_CERT_FILE "./cert.pem"
#define SERVER_PRIVATE_KEY_FILE "./key.pem"

#define LISTERN_HOST "localhost"
#define LISTERN_PORT 8080

int main(void) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
#else
httplib::Server svr;
#endif

if (!svr.is_valid()) {
printf("server has an error...\n");
return -1;
}

svr.set_error_handler([](const httplib::Request & /*req*/, httplib::Response &res) {
const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
char buf[BUFSIZ] = {0};
snprintf(buf, sizeof(buf), fmt, res.status);
res.set_content(buf, "text/html");
});

svr.set_logger([](const httplib::Request &req, const httplib::Response &res) {
printf("%s", log(req, res).c_str());
});

// router
RunRouter(svr);

// router test
RunRouterTest(svr);

svr.listen(LISTERN_HOST, LISTERN_PORT);

return 0;
}

router模块:

router.h

#ifndef __ROUTER_H__
#define __ROUTER_H__

#include "../http/httplib.h"

void RunRouter(httplib::Server& svr);

#endif // __ROUTER_H__

router.cc

#include "router.h"

void RunRouter(httplib::Server& svr)
{
svr.Post("/", [=](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("ok", "text/plain");
});

svr.Post("/ping", [](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("pong", "text/plain");
});

svr.Post("/slow", [](const httplib::Request & /*req*/, httplib::Response &res) {
std::this_thread::sleep_for(std::chrono::seconds(2));
res.set_content("Slow...\n", "text/plain");
});
}

router_test.h

#ifndef __TEST_API_H__
#define __TEST_API_H__

#include "../http/httplib.h"

void RunRouterTest(httplib::Server& svr);

#endif /* __TEST_API_H__ */

router_test.cc

#include <iostream>
#include "router_test.h"

void RunRouterTest(httplib::Server& svr)
{
// test data
svr.Post("/testData", [](const httplib::Request& req, httplib::Response &res) {
std::cout << "testData body: " << req.body << std::endl;

{
httplib::Client cli("localhost", 8080);
if (auto res = cli.Post("/ping")) {
std::cout << "res->status: " << res->status << std::endl;
std::cout << "res->get_header: " << res->get_header_value("Content-Type") << std::endl;
std::cout << "res->body: " << res->body << std::endl;
} else {
std::cout << "error code: " << res.error() << std::endl;
}
}

res.set_content(req.body.c_str(), "text/plain");
});
}

编译:

makefile:

#CXX = clang++
CXXFLAGS = -O2 -std=c++17 -I.. -Wall -Wextra -pthread

PREFIX = /usr/local
#PREFIX = $(shell brew --prefix)

OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto

ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz

BROTLI_DIR = $(PREFIX)/opt/brotli
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec

SRC = src/server/run.cc \
src/router/router.cc \
src/router/router_test.cc

TARGET = agent

all: ${TARGET}

agent : ${SRC} Makefile
$(CXX) -o ${TARGET} $(CXXFLAGS) ${SRC} $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)

pem:
openssl genrsa 2048 > key.pem
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem

clean:
rm ${TARGET} -f

build.sh

#!/bin/bash

# yum install brotli-devel openssl-devel -y

make clean

make -j4

功能测试:

root@localhost:~/work/ndb-influxdb-instance/src/agent# ./build.sh 
rm agent -f
g++ -o agent -O2 -std=c++2a -I.. -Wall -Wextra -pthread src/server/run.cc src/router/router.cc src/router/router_test.cc -DCPPHTTPLIB_ZLIB_SUPPORT -lz -DCPPHTTPLIB_BROTLI_SUPPORT -I/usr/local/opt/brotli/include -L/usr/local/opt/brotli/lib -lbrotlicommon -lbrotlienc -lbrotlidec
root@localhost:~/work/ndb-influxdb-instance/src/agent#
root@localhost:~/work/ndb-influxdb-instance/src/agent#
root@localhost:~/work/ndb-influxdb-instance/src/agent# ./agent
testData body: hello world
================================
POST HTTP/1.1 /ping
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8080
REMOTE_ADDR: ::1
REMOTE_PORT: 51950
User-Agent: cpp-httplib/0.10.1
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 4
Content-Type: text/plain

pong
res->status: 200
res->get_header: text/plain
res->body: pong
================================
POST HTTP/1.1 /testData?hello world=
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
Host: localhost:8080
REMOTE_ADDR: ::1
REMOTE_PORT: 51948
User-Agent: curl/7.78.0
--------------------------------
200 HTTP/1.1
Content-Length: 11
Content-Type: text/plain
Keep-Alive: timeout=5, max=5

hello world

root@localhost:~/work/ndb-influxdb-instance/src/agent/utest# ./ut_testData.sh 
curl -X POST --data hello world http://localhost:8080/testData
hello world

举报

相关推荐

0 条评论