目录
摘要:
说明如何快速搭建c++的web服务器, 便于后续使用c++编写web业务
总文件在: c++-web服务器,用来最小化研究用c++完成web服务器的功能-互联网文档类资源-CSDN下载
github:
GitHub - adofsauron/cpp-web-mini
依赖的库:
cpp-httplib:
备份地址:
cpp-httplib-0.10.2.tar.gz-互联网文档类资源-CSDN下载
编写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