Drogon C++ Framework

repository·master·Indexed 12 days ago

https://github.com/drogonframework/drogon

A high-performance, non-blocking C++ HTTP application framework for building scalable web applications and RESTful APIs. It utilizes modern C++ features, including coroutines and template-based reflection, to provide an asynchronous and decoupled development experience. Includes tools like drogon_ctl for project scaffolding and supports integrations with Redis, WebSockets, and Prometheus.

Tokens
19.9K
Snippets
60
Records
76
Agent score
95%

What's inside Drogon

  1. Overview of Drogon Framework

    master

    Drogon is a high-performance C++17/20 HTTP application framework designed for building various types of Web App servers. It is cross-platform, supporting Linux, macOS, FreeBSD/OpenBSD, HaikuOS, and Windows.

    Key Features:

    • High Performance: Uses a non-blocking IO framework based on epoll (or kqueue on macOS/FreeBSD).
    • Asynchronous Programming: Fully asynchronous logic and support for C++ coroutines.
    • Protocol Support: HTTP 1.0/1.1, HTTPS (via OpenSSL), and WebSocket (client and server).
    • Templating & Views: Supports CSP (C++ Server Pages) templates where C++ code is embedded in HTML, and dynamic loading of view pages via .so files.
    • Database Support: Asynchronous access to PostgreSQL, MySQL (MariaDB), Redis, and SQLite3 (via thread pool).
    • RESTful API Support: Built-in JSON request/response handling and flexible path-to-handler mapping.
    • Tooling: Includes drogon_ctl, a command-line tool to simplify controller and view code generation.
    • Advanced Features: Supports cookies, built-in sessions, Gzip/Brotli compression, pipelining, and a lightweight ORM.
  2. Implement HttpSimpleController

    master

    For standard web pages or simple logic, inherit from HttpSimpleController<T>. This allows you to define routes using the PATH_LIST macros. This approach is highly recommended for maintaining clean code and is the standard way to implement controllers.

    You can generate the boilerplate for these controllers using the command: drogon_ctl create controller <ControllerName>.

    /// The TestCtrl.h file
    #pragma once
    #include <drogon/HttpSimpleController.h>
    
    using namespace drogon;
    
    class TestCtrl : public HttpSimpleController<TestCtrl>
    {
    public:
        void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
        PATH_LIST_BEGIN
        PATH_ADD("/test", Get);
        PATH_LIST_END
    };
    
    /// The TestCtrl.cc file
    #include "TestCtrl.h"
    
    void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
                                          std::function<void (const HttpResponsePtr &)> &&callback)
    {
        HttpResponsePtr resp = HttpResponse::newHttpResponse();
        resp->setBody("<p>Hello, world!</p>");
        resp->setExpiredTime(0);
        callback(resp);
    }
  3. Implement HttpController for RESTful APIs

    master

    To build RESTful APIs, inherit from HttpController<T>. This class provides the METHOD_LIST macros, which allow you to map specific HTTP methods (Get, Post, etc.) and complex path parameters (e.g., /{id}) to specific member functions. This is the preferred way to handle parameterized routes and RESTful resource management.

    /// The header file
    #pragma once
    #include <drogon/HttpController.h>
    
    using namespace drogon;
    
    namespace api::v1
    {
    class User : public HttpController<User>
    {
      public:
        METHOD_LIST_BEGIN
        METHOD_ADD(User::getInfo, "/{id}", Get); 
        METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); 
        METHOD_ADD(User::newUser, "/{name}", Post); 
        METHOD_LIST_END
    
        void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
        void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
        void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
      public:
        User()
        {
            LOG_DEBUG << "User constructor!";
        }
    };
    }
  4. Initialize a Drogon application

    master

    A Drogon application can be initialized in several ways depending on the complexity of your requirements.

    1. Programmatic Configuration

    You can configure the application directly in main() using the app() singleton. This is useful for simple setups.

    2. Configuration via JSON File

    For production applications, it is recommended to use a JSON configuration file to keep the main() function clean.

    3. Inline Lambda Handlers

    For extremely simple logic, you can register handlers directly in main() using lambdas. However, this is not recommended for complex applications as it leads to unreadable code.

    // Programmatic configuration
    int main()
    {
        app().setLogPath("./")
             .setLogLevel(trantor::Logger::kWarn)
             .addListener("0.0.0.0", 80)
             .setThreadNum(16)
             .enableRunAsDaemon()
             .run();
    }
    
    // Configuration via JSON file
    int main()
    {
        app().loadConfigFile("./config.json").run();
    }
    
    // Inline Lambda Handler (not recommended for complex apps)
    app().registerHandler("/test?username={name}",
                        [](const HttpRequestPtr& req,
                           std::function<void (const HttpResponsePtr &)> &&callback,
                           const std::string &name) -> void
                        {
                            Json::Value json;
                            json["result"] = "ok";
                            json["message"] = "hello, " + name;
                            HttpResponsePtr resp = HttpResponse::newHttpJsonResponse(json);
                            callback(resp);
                        },
                        {Get, "LoginFilter"});
  5. Interact with the Redis Chat WebSocket API

    master

    The Redis Chat server uses a text-based protocol over WebSockets to manage rooms and messaging.

    Connecting

    Connect to the server using the WebSocket protocol, providing a name query parameter: ws://localhost:8080/chat?name=<your-name>

    Room Management

    • Enter a room: Send the command ENTER <roomNo> where <roomNo> is an integer between 0 and 99.
    • Quit a room: Send the command QUIT to leave the current room.

    Messaging

    • Send a message: Any text sent that does not match the ENTER or QUIT commands will be treated as a chat message within the current room.
  6. Explore Drogon usage examples

    master

    Drogon provides a variety of examples to demonstrate different capabilities of the framework, ranging from basic 'Hello World' applications to complex implementations involving WebSockets, Redis, and RESTful APIs. Use these examples to understand specific patterns for:

    • Basic Web Services: helloworld (multiple ways to implement a basic server).
    • Client-side Operations: client_example (HTTP client) and websocket_client (WebSocket client).
    • Session & Security: login_session (built-in session management) and cors (Cross-Origin Resource Sharing).
    • Data & Storage: redis (Redis integration), redis_cache (using coroutines with Redis), jsonstore (building RESTful APIs with in-memory storage), and redis_chat (combining WebSockets with Redis pub/sub).
    • Advanced Networking: file_upload (handling file uploads), simple_reverse_proxy (implementing an HTTP reverse proxy with round-robin), and websocket_server (a chat room server).
    • Observability & Performance: prometheus_example (using the Prometheus exporter) and benchmark (basic performance testing).
  7. Build the Drogon Alpine Docker Image

    master

    To build a custom Drogon development environment using Alpine Linux, navigate to the drogon/docker/alpine directory within the repository and run the docker build command. Passing your current user's UID and GID as build arguments ensures that files created within the container have the correct permissions on your host machine.

    $ cd drogon/docker/alpine # from this repository
    $ docker build --no-cache --build-arg UID=`id -u` --build-arg GID=`id -g` -t drogon-alpine .
  8. Build a Drogon project using Docker

    master

    To compile your Drogon project within the containerized environment, run cmake and make inside a build directory. The command mounts the project directory and sets the working directory to the build folder.

    $ cd hello_world
    $ docker run --rm --volume="$PWD:/drogon/app" -w="/drogon/app/build" drogon-alpine sh -c "cmake .. && make"
  9. Build the Redis cache example with coroutine support

    master

    To use the Redis client examples in Drogon, you must use a compiler that supports C++20 coroutines. Configure your project using cmake with the appropriate CMAKE_CXX_FLAGS based on your compiler version:

    • GCC 10: Use -std=c++20 -fcoroutines
    • GCC 11+: Use -std=c++20
    • MSVC 16.25+: Use /std:c++20

    Additionally, ensure a Redis server is running on the default port 6379.

    # For GCC 10
    cmake .. -DCMAKE_CXX_FLAGS="-std=c++20 -fcoroutines"
    
    # For GCC >= 11
    cmake .. -DCMAKE_CXX_FLAGS="-std=c++20"
    
    # For MSVC >= 16.25
    cmake .. -DCMAKE_CXX_FLAGS="/std:c++20"
  10. Start the Drogon server in Docker

    master

    Run your compiled Drogon application using the drogon-alpine container. This command maps host port 8080 to container port 80 and runs the process in the background (-d). It also mounts the build directory so the container can access the executable.

    $ docker run --name drogon_test --rm -u 0 -v="$PWD/build:/drogon/app" -w="/drogon/app" -p 8080:80 -d drogon-alpine ./hello_world
  11. Cross-compile Drogon

    master

    Drogon supports cross-compilation. You must define the CMAKE_SYSTEM_NAME in your toolchain file. For example, to target Linux on ARM:

    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR arm)

    You can also disable the building of drogon_ctl and examples in your toolchain file by setting BUILD_EXAMPLES and BUILD_CTL to OFF.