{"id":21068938,"url":"https://github.com/kamchatka-volcano/fcgi_responder","last_synced_at":"2025-06-24T06:06:00.910Z","repository":{"id":37007051,"uuid":"301688709","full_name":"kamchatka-volcano/fcgi_responder","owner":"kamchatka-volcano","description":"C++ FastCGI protocol implementation without networking API dependencies ","archived":false,"fork":false,"pushed_at":"2024-07-14T08:58:09.000Z","size":259,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-03T20:04:57.562Z","etag":null,"topics":["cpp17","fastcgi","fcgi"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"ms-pl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kamchatka-volcano.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-06T10:21:14.000Z","updated_at":"2024-06-28T10:27:39.000Z","dependencies_parsed_at":"2023-01-17T12:44:49.970Z","dependency_job_id":"dd465ae9-fc02-454d-9dc0-32faf977a7e9","html_url":"https://github.com/kamchatka-volcano/fcgi_responder","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/kamchatka-volcano/fcgi_responder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamchatka-volcano%2Ffcgi_responder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamchatka-volcano%2Ffcgi_responder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamchatka-volcano%2Ffcgi_responder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamchatka-volcano%2Ffcgi_responder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kamchatka-volcano","download_url":"https://codeload.github.com/kamchatka-volcano/fcgi_responder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamchatka-volcano%2Ffcgi_responder/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261618105,"owners_count":23185091,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp17","fastcgi","fcgi"],"created_at":"2024-11-19T18:29:31.478Z","updated_at":"2025-06-24T06:06:00.886Z","avatar_url":"https://github.com/kamchatka-volcano.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![build \u0026 test (clang, gcc, MSVC)](https://github.com/kamchatka-volcano/fcgi_responder/actions/workflows/build_and_test.yml/badge.svg?branch=master)](https://github.com/kamchatka-volcano/fcgi_responder/actions/workflows/build_and_test.yml)\n\n`fcgi_responder` is a C++17 library that implements the [Responder role](https://fast-cgi.github.io/spec#62-responder) of the FastCGI protocol. It processes the raw data received from the web server and returns serialized output that needs to be sent back to the server by the client code. The library does not handle the implementation of the connection to the web server, so clients can use whatever socket programming methods they prefer. This makes `fcgi_responder` portable and free of external dependencies.\n\n`fcgi_responder` aims to be a modern and readable implementation of the FastCGI protocol. Our benchmark using the `asio` library shows more than 100% increase in performance compared to the popular FastCGI implementation `libfcgi`, which is written in C.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg height=\"372\" src=\"doc/fcgi_responder_vs_libfcgi.png\"/\u003e  \n\u003c/p\u003e\n\n## Showcase\n* [asyncgi](https://github.com/kamchatka-volcano/asyncgi/) - a web framework based on `fcgi_responder` and `Asio`\n\n## Usage\n### Processing requests from the web server\nTo use the `fcgi_responder` library, inherit from the `fcgi::Responder` class and implement its pure virtual methods to provide the necessary functionality:\n\n* `virtual void sendData(const std::string\u0026 data) = 0`\n* `virtual void processRequest(fcgi::Request\u0026\u0026 request, fcgi::Response\u0026\u0026 response) = 0`\n* `virtual void disconnect() = 0`\n\nThen, listen for connections from the web server and process the incoming data by passing it to the `fcgi::Responder::receiveData` method.\n\nThis is a minimal example that uses the standalone asio library for networking:\n```C++\n#include \"asio.hpp\"\n#include \u003cfcgi_responder/responder.h\u003e\n#include \u003ciostream\u003e\n\nusing unixdomain = asio::local::stream_protocol;\n\nclass Connection : public fcgi::Responder{\npublic:\n    explicit Connection(unixdomain::socket\u0026\u0026 socket)\n    : socket_(std::move(socket))\n    {\n    }\n\n    void process()\n    {\n        while(isOpened_){\n            try {\n                auto receivedDataSize = socket_.read_some(asio::buffer(buffer_));\n                ///\n                /// Passing read socket data with fcgi::Responder::receiveData method\n                ///\n                receiveData(buffer_.data(), receivedDataSize);\n            }\n            catch(...){\n                isOpened_ = false;\n                return;\n            }\n        }\n    }\n\nprivate:\n    ///\n    /// Overriding fcgi::Responder::sendData to send response data to the web server\n    ///\n    void sendData(const std::string\u0026 data) override\n    {\n        asio::write(socket_, asio::buffer(data, data.size()));\n    }\n\n    ///\n    /// Overriding fcgi::Responder::disconnect to close connection with the web server\n    ///\n    void disconnect() override\n    {\n        try{\n            socket_.shutdown(unixdomain::socket::shutdown_both);\n            socket_.close();\n        }\n        catch(const std::system_error\u0026 e){\n            std::cerr \u003c\u003c \"socket close error:\" \u003c\u003c e.code();\n        }\n        isOpened_ = false;\n    };\n\n    ///\n    /// Overriding fcgi::Responder::processRequest to form response data\n    ///\n    void processRequest(fcgi::Request\u0026\u0026, fcgi::Response\u0026\u0026 response) override\n    {\n        response.setData(\"Status: 200 OK\\r\\n\"\n                         \"Content-Type: text/html\\r\\n\"\n                         \"\\r\\n\"\n                         \"HELLO WORLD USING ASIO!\");\n        response.send();\n    }\n\nprivate:\n    unixdomain::socket socket_;\n    std::array\u003cchar, 65536\u003e buffer_;\n    bool isOpened_ = true;\n};\n\nint main ()\n{\n    auto socketPath = std::string{\"/tmp/fcgi.sock\"};\n    umask(0);\n    chmod(socketPath.c_str(), 0777);\n    unlink(socketPath.c_str());\n\n    auto io = asio::io_context{};\n    auto acceptor = unixdomain::acceptor{io, unixdomain::endpoint{socketPath}};\n\n    while (true) {\n        auto socket = acceptor.accept();\n        auto connection = Connection{std::move(socket)};\n        connection.process();\n    }\n    return 0;\n}\n```\nCheck the `examples` directory for this and other example that uses the Qt framework.\n\n### Sending requests to FastCGI applications\nThe `fcgi_responder` library provides a `fcgi::Requester` class that can be used to send requests to FastCGI applications.\n\nTo use it, inherit from the `fcgi::Requester` class and implement its pure virtual methods:\n- `virtual void sendData(const std::string\u0026 data) = 0`\n- `virtual void disconnect() = 0`\n\nOnce you have done that, you can connect the socket to the listening FastCGI application and make a request by calling the `fcgi::Requester::sendRequest` method. Be sure to process incoming data by passing it to the `fcgi::Requester::receiveData` method.\n\n\nThis is a minimal example that uses the standalone asio library for networking:\n```C++\n#include \"asio.hpp\"\n#include \u003cfcgi_responder/requester.h\u003e\n#include \u003ciostream\u003e\n\nusing unixdomain = asio::local::stream_protocol;\n\nclass Client : public fcgi::Requester{\npublic:\n    explicit Client(unixdomain::socket\u0026\u0026 socket)\n    : socket_(std::move(socket))\n    {\n    }\n\n    void process()\n    {\n        while(isOpened_){\n            auto receivedDataSize = socket_.read_some(asio::buffer(buffer_));\n            ///\n            /// Passing read socket data with fcgi::Requester::receiveData method\n            ///\n            receiveData(buffer_.data(), receivedDataSize);\n        }\n    }\n\nprivate:\n    ///\n    /// Overriding fcgi::Requester::sendData to send request data to the FastCGI application\n    ///\n    void sendData(const std::string\u0026 data) override\n    {\n        asio::write(socket_, asio::buffer(data, data.size()));\n    }\n\n    ///\n    /// Overriding fcgi::Requester::disconnect to close connection with the FastCGI application\n    ///\n    void disconnect() override\n    {\n        try{\n            socket_.shutdown(unixdomain::socket::shutdown_both);\n            socket_.close();\n        }\n        catch(const std::system_error\u0026 e){\n            std::cerr \u003c\u003c \"socket close error:\" \u003c\u003c e.code();\n        }\n        isOpened_ = false;\n    };\n\nprivate:\n    unixdomain::socket socket_;\n    std::array\u003cchar, 65536\u003e buffer_;\n    bool isOpened_ = true;\n};\n\nvoid onResponseReceived(const std::optional\u003cfcgi::ResponseData\u003e\u0026 response)\n{\n    std::cout \u003c\u003c \"Response:\" \u003c\u003c std::endl;\n    if (response)\n        std::cout \u003c\u003c response-\u003edata \u003c\u003c std::endl;\n    else\n        std::cout \u003c\u003c \"No response\" \u003c\u003c std::endl;\n}\n\nint main ()\n{\n    auto socketPath = std::string{\"/tmp/fcgi.sock\"};\n    auto io = asio::io_context{};\n    auto socket = unixdomain::socket{io};\n    try {\n        socket.connect(unixdomain::endpoint{socketPath});\n    }\n    catch(std::system_error\u0026 e){\n        std::cerr \u003c\u003c \"Socket connection error:\" \u003c\u003c e.code();\n        return 1;\n    }\n\n    auto client = Client{std::move(socket)};\n    client.setErrorInfoHandler([](const std::string\u0026 error){\n        std::cout \u003c\u003c error \u003c\u003c std::endl;\n    });\n    client.sendRequest({{\"REQUEST_METHOD\",\"GET\"},\n                        {\"REMOTE_ADDR\",\"127.0.0.1\"},\n                        {\"HTTP_HOST\",\"localhost\"},\n                        {\"REQUEST_URI\",\"/\"}}, {}, onResponseReceived);\n    client.process();\n    return 0;\n}\n```\n\n## Installation\nDownload and link the library from your project's CMakeLists.txt:\n```\ncmake_minimum_required(VERSION 3.14)\n\ninclude(FetchContent)\n\nFetchContent_Declare(fcgi_responder\n    GIT_REPOSITORY \"https://github.com/kamchatka-volcano/fcgi_responder.git\"\n    GIT_TAG \"origin/master\"\n)\n#uncomment if you need to install fcgi_responder with your target\n#set(INSTALL_FCGI_RESPONDER ON)\nFetchContent_MakeAvailable(fcgi_responder)\n\nadd_executable(${PROJECT_NAME})\ntarget_link_libraries(${PROJECT_NAME} PRIVATE fcgi_responder::fcgi_responder)\n```\n\nTo install the library system-wide, use the following commands:\n```\ngit clone https://github.com/kamchatka-volcano/fcgi_responder.git\ncd fcgi_responder\ncmake -S . -B build\ncmake --build build\ncmake --install build\n```\n\nAfter installation, you can use the find_package() command to make the installed library available inside your project:\n```\nfind_package(fcgi_responder 1.0.0 REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE fcgi_responder::fcgi_responder)\n```\n\n## Running tests\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_TESTS=ON\ncmake --build build \ncd build/tests \u0026\u0026 ctest\n```\n\n## Running fuzzing tests\n`fcgi_responder` is tested with the `AFL++` fuzzing testing tool. This repository contains fuzzing input data in `fuzz_test/input`, a fuzzing harness `fcgi_responder_fuzzer` and a fuzzing input data generator `fuzz_input_generator`.  \nTo build `fcgi_responder_fuzzer` for debugging input data run the following commands:\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_FUZZ_TESTS=ON\ncmake --build build\n```\n\nTo build `fcgi_responder_fuzzer` for running fuzzing tests with `afl-fuzz` utility run the following commands:\n```\ncd fcgi_responder\nLLVM_CONFIG=\"llvm-config-11\" CXX=afl-clang-fast++ cmake -S . -B afl_build -DENABLE_FUZZ_TESTS=ON\ncmake --build afl_build\n```\nAdjust the `LLVM_CONFIG` variable to point to the version of LLVM you want to use.\n\nTo run fuzzing tests with the `afl-fuzz` utility run the following command:\n```\nafl-fuzz -i ./fuzz_tests/input -o ./fuzz_tests/res  -x ./fuzz_tests/afl_dict.txt -s 111 -- ./afl_build/fuzz_tests/fcgi_responder_fuzzer @@\n```\n\nThe results of the fuzzing tests can be found in the `fuzz_tests/res` directory.\n\nTo learn more about fuzzing testing, check out the [AFL++ docs](https://github.com/AFLplusplus/AFLplusplus/tree/stable/docs) and this [tutorial](https://github.com/antonio-morales/Fuzzing101/).\n\nThe input data inside `/fuzz_tests/input` is generated by the `fuzz_input_generator` utility. It is a modified version of the unit tests that writes input data to files. To build it, use the following commands:\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_FUZZ_INPUT_GENERATOR=ON\ncmake --build build\n```\n\n\n## Running examples\nSet up your webserver to use the FastCGI protocol over unix domain socket `/tmp/fcgi.sock`. With NGINX you can use this config:\n\n```\nserver {\n\tlisten 8088;\n\tserver_name localhost;\n\tindex /~;\n\n\tlocation / {\n\t\ttry_files $uri $uri/ @fcgi;\n\t}\n\t\n\tlocation @fcgi {\n\t\tfastcgi_pass  unix:/tmp/fcgi.sock;\n\t\tinclude fastcgi_params;\n\t\tfastcgi_intercept_errors on;\n\t\tfastcgi_keep_conn off;\n\t}\n}\n\n```\n\nBuild and run asio example:\n\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_ASIO_EXAMPLE=ON -DENABLE_ASIO_REQUESTER_EXAMPLE=ON\ncmake --build build \n./build/examples/asio_example\n./build/examples/asio_requester_example\n```\n\nOr build and run Qt example:\n\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_QT_EXAMPLE=ON -DENABLE_QT_REQUESTER_EXAMPLE=ON\ncmake --build build \n./build/examples/qt_example\n./build/examples/qt_requester_example\n```\n\nCheck that it's working here: http://localhost:8088 \n\n\n## Running benchmarks\nUtilities `libfcgi_benchmark` and `fcgi_responder_benchmark` were used to measure performance for the chart in this document. They can be built with the following commands:\n\n```\ncd fcgi_responder\ncmake -S . -B build -DENABLE_LIBFCGI_BENCHMARK=ON -DENABLE_FCGI_RESPONDER_BENCHMARK=ON\ncmake --build build\n./build/utils/fcgi_responder_benchmark/fcgi_responder_benchmark --response-size 27\n./build/utils/libfcgi_benchmark/libfcgi_benchmark --response-size 27\n``` \n\nThe required webserver configuration is the same as in the previous section.\n\nThroughput performance of both benchmarks can be measured with `ab` tool:\n\n```\nab -n 20000 -c 10 http://localhost:8088/\n```\n\n\n### License\n**fcgi_responder** is licensed under the [MS-PL license](/LICENSE.md)  \n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamchatka-volcano%2Ffcgi_responder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkamchatka-volcano%2Ffcgi_responder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamchatka-volcano%2Ffcgi_responder/lists"}