fcgi_responder是C ++ 17库,它实现了FastCGI协议的响应者角色。它处理从Web服务器接收到的原始数据,并返回需要通过客户端代码将其发送回服务器的序列化输出。该库不处理与Web服务器的连接的实现,因此客户端可以使用他们喜欢的任何套接字编程方法。这使得fcgi_responder可移植并不受外部依赖关系。
fcgi_responder目标是成为FastCGI协议的现代且可读性的实现。与流行的FastCGI实施libfcgi相比,我们使用asio库的基准显示出100%以上的性能增长,该实现是用C编写的。
展示柜
- 异步 - 基于
fcgi_responder和Asio网络框架
用法
Web服务器的处理请求
要使用fcgi_responder库,请从fcgi::Responder类中继承并实现其纯虚拟方法以提供必要的功能:
-
virtual void sendData(const std::string& data) = 0 -
virtual void processRequest(fcgi::Request&& request, fcgi::Response&& response) = 0 -
virtual void disconnect() = 0
然后,通过将传入数据传递到fcgi::Responder::receiveData方法来聆听Web服务器的连接并处理传入数据。
这是一个最小的示例,使用独立的ASIO库进行网络:
# include " asio.hpp "
# include < fcgi_responder /responder.h >
# include < iostream >
using unixdomain = asio::local::stream_protocol;
class Connection : public fcgi ::Responder{
public:
explicit Connection (unixdomain::socket&& socket)
: socket_(std::move(socket))
{
}
void process ()
{
while (isOpened_){
try {
auto receivedDataSize = socket_. read_some ( asio::buffer (buffer_));
// /
// / Passing read socket data with fcgi::Responder::receiveData method
// /
receiveData (buffer_. data (), receivedDataSize);
}
catch (...){
isOpened_ = false ;
return ;
}
}
}
private:
// /
// / Overriding fcgi::Responder::sendData to send response data to the web server
// /
void sendData ( const std::string& data) override
{
asio::write (socket_, asio::buffer (data, data. size ()));
}
// /
// / Overriding fcgi::Responder::disconnect to close connection with the web server
// /
void disconnect () override
{
try {
socket_. shutdown (unixdomain::socket::shutdown_both);
socket_. close ();
}
catch ( const std::system_error& e){
std::cerr << " socket close error: " << e. code ();
}
isOpened_ = false ;
};
// /
// / Overriding fcgi::Responder::processRequest to form response data
// /
void processRequest (fcgi::Request&&, fcgi::Response&& response) override
{
response. setData ( " Status: 200 OK \r\n "
" Content-Type: text/html \r\n "
" \r\n "
" HELLO WORLD USING ASIO! " );
response. send ();
}
private:
unixdomain::socket socket_;
std::array< char , 65536 > buffer_;
bool isOpened_ = true ;
};
int main ()
{
auto socketPath = std::string{ " /tmp/fcgi.sock " };
umask ( 0 );
chmod (socketPath. c_str (), 0777 );
unlink (socketPath. c_str ());
auto io = asio::io_context{};
auto acceptor = unixdomain::acceptor{io, unixdomain::endpoint{socketPath}};
while ( true ) {
auto socket = acceptor. accept ();
auto connection = Connection{ std::move (socket)};
connection. process ();
}
return 0 ;
}
检查使用QT框架的examples目录和其他示例。
向FastCGI应用程序发送请求
fcgi_responder库提供了一个可以用于将请求发送到FastCGI应用程序的fcgi::Requester类。
要使用它,从fcgi::Requester类中继承并实现其纯虚拟方法:
-
virtual void sendData(const std::string& data) = 0 -
virtual void disconnect() = 0
完成此操作后,您可以将套接字连接到侦听FastCGI应用程序,并通过调用fcgi::Requester::sendRequest方法来提出请求。请确保通过将传入数据传递到fcgi::Requester::receiveData方法来处理。
这是一个最小的示例,使用独立的ASIO库进行网络:
# include " asio.hpp "
# include < fcgi_responder /requester.h >
# include < iostream >
using unixdomain = asio::local::stream_protocol;
class Client : public fcgi ::Requester{
public:
explicit Client (unixdomain::socket&& socket)
: socket_(std::move(socket))
{
}
void process ()
{
while (isOpened_){
auto receivedDataSize = socket_. read_some ( asio::buffer (buffer_));
// /
// / Passing read socket data with fcgi::Requester::receiveData method
// /
receiveData (buffer_. data (), receivedDataSize);
}
}
private:
// /
// / Overriding fcgi::Requester::sendData to send request data to the FastCGI application
// /
void sendData ( const std::string& data) override
{
asio::write (socket_, asio::buffer (data, data. size ()));
}
// /
// / Overriding fcgi::Requester::disconnect to close connection with the FastCGI application
// /
void disconnect () override
{
try {
socket_. shutdown (unixdomain::socket::shutdown_both);
socket_. close ();
}
catch ( const std::system_error& e){
std::cerr << " socket close error: " << e. code ();
}
isOpened_ = false ;
};
private:
unixdomain::socket socket_;
std::array< char , 65536 > buffer_;
bool isOpened_ = true ;
};
void onResponseReceived ( const std::optional<fcgi::ResponseData>& response)
{
std::cout << " Response: " << std::endl;
if (response)
std::cout << response-> data << std::endl;
else
std::cout << " No response " << std::endl;
}
int main ()
{
auto socketPath = std::string{ " /tmp/fcgi.sock " };
auto io = asio::io_context{};
auto socket = unixdomain::socket{io};
try {
socket. connect (unixdomain::endpoint{socketPath});
}
catch (std::system_error& e){
std::cerr << " Socket connection error: " << e. code ();
return 1 ;
}
auto client = Client{ std::move (socket)};
client. setErrorInfoHandler ([]( const std::string& error){
std::cout << error << std::endl;
});
client. sendRequest ({{ " REQUEST_METHOD " , " GET " },
{ " REMOTE_ADDR " , " 127.0.0.1 " },
{ " HTTP_HOST " , " localhost " },
{ " REQUEST_URI " , " / " }}, {}, onResponseReceived);
client. process ();
return 0 ;
}
安装
从项目的cmakelists.txt下载并链接库:
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
FetchContent_Declare( fcgi_responder
GIT_REPOSITORY "https://gith**u*b.com/kamchatka-volcano/fcgi_responder.git"
GIT_TAG "origin/master"
)
#uncomment if you need to install fcgi_responder with your target
#set(INSTALL_ fcgi_responder ON)
FetchContent_MakeAvailable( fcgi_responder )
add_executable(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE fcgi_responder :: fcgi_responder )
要安装整个系统范围的库,请使用以下命令:
git clone https://gith**u*b.com/kamchatka-volcano/fcgi_responder.git
cd fcgi_responder
cmake -S . -B build
cmake --build build
cmake --install build
安装后,您可以使用find_package()命令使您的项目内有可用的库:
find_package( fcgi_responder 1.0.0 REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE fcgi_responder :: fcgi_responder )
运行测试
cd fcgi_responder
cmake -S . -B build -DENABLE_TESTS=ON
cmake --build build
cd build/tests && ctest
运行模糊测试
使用AFL++模糊测试工具对fcgi_responder进行了测试。该存储库包含fuzz_test/input中的输入数据,一个模糊的线束fcgi_responder _fuzzer和FUZZing Input Data Enput Data Generator fuzz_input_generator 。
要构建fcgi_responder _fuzzer用于调试输入数据,请运行以下命令:
cd fcgi_responder
cmake -S . -B build -DENABLE_FUZZ_TESTS=ON
cmake --build build
要构建fcgi_responder _fuzzer以运行使用afl-fuzz实用程序运行fuzzsing测试,请运行以下命令:
cd fcgi_responder
LLVM_CONFIG="llvm-config-11" CXX=afl-clang-fast++ cmake -S . -B afl_build -DENABLE_FUZZ_TESTS=ON
cmake --build afl_build
调整LLVM_CONFIG变量,以指向您要使用的LLVM的版本。
使用afl-fuzz实用程序运行以下命令:
afl-fuzz -i ./fuzz_tests/input -o ./fuzz_tests/res -x ./fuzz_tests/afl_dict.txt -s 111 -- ./afl_build/fuzz_tests/ fcgi_responder _fuzzer @@
模糊测试的结果可以在fuzz_tests/res目录中找到。
要了解有关模糊测试的更多信息,请查看AFL ++文档和本教程。
fuzz_input_generator实用程序生成/fuzz_tests/input内部的输入数据。这是将输入数据写入文件的单元测试的修改版本。要构建它,请使用以下命令:
cd fcgi_responder
cmake -S . -B build -DENABLE_FUZZ_INPUT_GENERATOR=ON
cmake --build build
运行示例
设置您的Web服务器以使用Unix域套接字/tmp/fcgi.sock上的FastCGI协议。使用nginx,您可以使用此配置:
server {
listen 8088;
server_name localhost;
index /~;
location / {
try_files $uri $uri/ @fcgi;
}
location @fcgi {
fastcgi_pass unix:/tmp/fcgi.sock;
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_keep_conn off;
}
}
构建并运行ASIO示例:
cd fcgi_responder
cmake -S . -B build -DENABLE_ASIO_EXAMPLE=ON -DENABLE_ASIO_REQUESTER_EXAMPLE=ON
cmake --build build
./build/examples/asio_example
./build/examples/asio_requester_example
或构建并运行QT示例:
cd fcgi_responder
cmake -S . -B build -DENABLE_QT_EXAMPLE=ON -DENABLE_QT_REQUESTER_EXAMPLE=ON
cmake --build build
./build/examples/qt_example
./build/examples/qt_requester_example
检查它在这里工作:http:// localhost:8088
运行基准
实用程序libfcgi_benchmark和fcgi_responder _benchmark用于测量本文档中图表的性能。它们可以通过以下命令构建:
cd fcgi_responder
cmake -S . -B build -DENABLE_LIBFCGI_BENCHMARK=ON -DENABLE_ fcgi_responder _BENCHMARK=ON
cmake --build build
./build/utils/ fcgi_responder _benchmark/ fcgi_responder _benchmark --response-size 27
./build/utils/libfcgi_benchmark/libfcgi_benchmark --response-size 27
所需的Web服务器配置与上一节相同。
可以使用ab工具来测量这两个基准的吞吐量性能:
ab -n 20000 -c 10 http://l*ocalh**ost:8088/
执照
fcgi_responder由MS-PL许可证获得许可
通过命令行克隆项目: