File: main.cpp

package info (click to toggle)
boost1.88 1.88.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 576,932 kB
  • sloc: cpp: 4,149,234; xml: 136,789; ansic: 35,092; python: 33,910; asm: 5,698; sh: 4,604; ada: 1,681; makefile: 1,633; pascal: 1,139; perl: 1,124; sql: 640; yacc: 478; ruby: 271; java: 77; lisp: 24; csh: 6
file content (189 lines) | stat: -rw-r--r-- 6,062 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//
// Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/awaitable.hpp>
#include <boost/pfr/config.hpp>
#if defined(BOOST_ASIO_HAS_CO_AWAIT) && BOOST_PFR_CORE_NAME_ENABLED

//[example_http_server_cpp20_main_cpp

/**
 * Implements a HTTP REST API using Boost.MySQL and Boost.Beast.
 * The API models a simplified order management system for an online store.
 * Using the API, users can query the store's product catalog, create and
 * edit orders, and check them out for payment.
 *
 * The API defines the following endpoints:
 *
 *    GET    /products?search={s}       Returns a list of products
 *    GET    /orders                    Returns all orders
 *    GET    /orders?id={}              Returns a single order
 *    POST   /orders                    Creates a new order
 *    POST   /orders/items              Adds a new order item to an existing order
 *    DELETE /orders/items?id={}        Deletes an order item
 *    POST   /orders/checkout?id={}     Checks out an order
 *    POST   /orders/complete?id={}     Completes an order
 *
 * Each order can have any number of order items. An order item
 * represents an individual product that has been added to an order.
 * Orders are created empty, in a 'draft' state. Items can then be
 * added and removed from the order. After adding the desired items,
 * orders can be checked out for payment. A third-party service, like Stripe,
 * would be used to collect the payment. For simplicity, we've left this part
 * out of the example. Once checked out, an order is no longer editable.
 * Finally, after successful payment, order are transitioned to the
 * 'complete' status.
 *
 * The server uses C++20 coroutines and is multi-threaded.
 * It also requires linking to Boost::json and Boost::url.
 * The database schema is defined in db_setup.sql, in the same directory as this file.
 * You need to source this file before running the example.
 */

#include <boost/mysql/any_address.hpp>
#include <boost/mysql/connection_pool.hpp>
#include <boost/mysql/pool_params.hpp>

#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/system/error_code.hpp>

#include <cstddef>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <string>

#include "server.hpp"

using namespace orders;
namespace mysql = boost::mysql;
namespace asio = boost::asio;

// The number of threads to use
static constexpr std::size_t num_threads = 5;

int main_impl(int argc, char* argv[])
{
    // Check command line arguments.
    if (argc != 5)
    {
        std::cerr << "Usage: " << argv[0] << " <username> <password> <mysql-hostname> <port>\n";
        return EXIT_FAILURE;
    }

    // Application config
    const char* mysql_username = argv[1];
    const char* mysql_password = argv[2];
    const char* mysql_hostname = argv[3];
    auto port = static_cast<unsigned short>(std::stoi(argv[4]));

    // An event loop, where the application will run.
    // We will use the main thread to run the pool, too, so we use
    // one thread less than configured
    asio::thread_pool th_pool(num_threads - 1);

    // Create a connection pool
    mysql::connection_pool pool(
        // Use the thread pool as execution context
        th_pool,

        // Pool configuration
        mysql::pool_params{
            // Connect using TCP, to the given hostname and using the default port
            .server_address = mysql::host_and_port{mysql_hostname},

            // Authenticate using the given username
            .username = mysql_username,

            // Password for the above username
            .password = mysql_password,

            // Database to use when connecting
            .database = "boost_mysql_orders",

            // We're using multi-queries
            .multi_queries = true,

            // Using thread_safe will make the pool thread-safe by internally
            // creating and using a strand.
            // This allows us to share the pool between sessions, which may run
            // concurrently, on different threads.
            .thread_safe = true,
        }
    );

    // Launch the MySQL pool
    pool.async_run(asio::detached);

    // A signal_set allows us to intercept SIGINT and SIGTERM and
    // exit gracefully
    asio::signal_set signals{th_pool.get_executor(), SIGINT, SIGTERM};

    // Capture SIGINT and SIGTERM to perform a clean shutdown
    signals.async_wait([&th_pool](boost::system::error_code, int) {
        // Stop the execution context. This will cause main to exit
        th_pool.stop();
    });

    // Start listening for HTTP connections. This will run until the context is stopped
    asio::co_spawn(
        // Use the thread pool to run the listener coroutine
        th_pool,

        // The coroutine to run
        [&pool, port] { return run_server(pool, port); },

        // If an exception is thrown in the listener coroutine, propagate it
        [](std::exception_ptr exc) {
            if (exc)
                std::rethrow_exception(exc);
        }
    );

    // Attach the current thread to the thread pool. This will block
    // until stop() is called
    th_pool.attach();

    // Wait until all threads have exited
    th_pool.join();

    std::cout << "Server exiting" << std::endl;

    // (If we get here, it means we got a SIGINT or SIGTERM)
    return EXIT_SUCCESS;
}

int main(int argc, char** argv)
{
    try
    {
        main_impl(argc, argv);
    }
    catch (const std::exception& err)
    {
        std::cerr << "Error: " << err.what() << std::endl;
        return 1;
    }
}

//]

#else

#include <iostream>

int main()
{
    std::cout << "Sorry, your compiler doesn't have the required capabilities to run this example"
              << std::endl;
}

#endif