Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an HTTP server example using C++20 coroutines #385

Open
wants to merge 46 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
57cb218
Initial prototype
anarthal Dec 1, 2024
96c7e46
Repository (1)
anarthal Dec 6, 2024
d93bf07
repository (2)
anarthal Dec 6, 2024
62b27e2
Finished repository
anarthal Dec 6, 2024
91b646d
return without reset
anarthal Dec 6, 2024
512c132
Remove PFR include and check
anarthal Dec 9, 2024
fe096c2
error.hpp/cpp
anarthal Dec 13, 2024
2c6e4be
Update log_error includes
anarthal Dec 15, 2024
43e3e90
Other include guards
anarthal Dec 15, 2024
3b6c2d0
Handler bodies
anarthal Dec 15, 2024
724b30d
handle_request common part
anarthal Dec 15, 2024
8796420
Simplify JSON request parsing
anarthal Dec 15, 2024
a84e3e9
request_data
anarthal Dec 15, 2024
cd9388c
Proper error logging
anarthal Dec 15, 2024
c373d7f
locking std::cerr impl
anarthal Dec 15, 2024
adf293e
Unify invalid status error
anarthal Dec 15, 2024
600bf4a
Repo docs and error impl 1
anarthal Dec 15, 2024
424796b
Sanitize the DELETE
anarthal Dec 15, 2024
52e2482
C++20 guards
anarthal Dec 16, 2024
0b25b8f
error.hpp comments
anarthal Dec 16, 2024
0fbc46d
update comment
anarthal Dec 16, 2024
4e07761
Include trimming
anarthal Dec 16, 2024
f64660c
Fix build error
anarthal Dec 16, 2024
2770c85
split db_setup
anarthal Dec 16, 2024
ec6bf81
Fix static iface failure
anarthal Dec 16, 2024
885e239
run_orders prototype
anarthal Dec 16, 2024
2a912dc
Error case and helpers
anarthal Dec 16, 2024
3ba826b
Missing multi_queries
anarthal Dec 16, 2024
ceddea4
Order lifecycle test
anarthal Dec 16, 2024
55209be
remove items test
anarthal Dec 16, 2024
d76963f
test get orders
anarthal Dec 16, 2024
a0a0dc2
get order by id test
anarthal Dec 16, 2024
50c7a9b
Invalid ID for get_order
anarthal Dec 16, 2024
a54731f
invalid content type
anarthal Dec 16, 2024
8a0ab7b
more error tests
anarthal Dec 16, 2024
869b7f2
add order item not found
anarthal Dec 16, 2024
f747458
switch to unprocessable entity
anarthal Dec 16, 2024
e43758c
order not editable
anarthal Dec 16, 2024
c399107
remove order item errors
anarthal Dec 16, 2024
48304c3
checkout/complete order tests
anarthal Dec 16, 2024
0f60655
Finished tests
anarthal Dec 16, 2024
b379764
runner to cmake
anarthal Dec 16, 2024
8793f30
runner cleanup
anarthal Dec 16, 2024
7b58088
Bug in method_not_allowed
anarthal Dec 16, 2024
845f01b
Jamfile
anarthal Dec 16, 2024
0ba8b09
Add to qbks
anarthal Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/qbk/00_main.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ END
[import ../../example/2_simple/patch_updates.cpp]
[import ../../example/2_simple/source_script.cpp]
[import ../../example/2_simple/pipeline.cpp]
[import ../../example/3_advanced/http_server_cpp20/main.cpp]
[import ../../example/3_advanced/http_server_cpp20/types.hpp]
[import ../../example/3_advanced/http_server_cpp20/error.hpp]
[import ../../example/3_advanced/http_server_cpp20/error.cpp]
[import ../../example/3_advanced/http_server_cpp20/repository.hpp]
[import ../../example/3_advanced/http_server_cpp20/repository.cpp]
[import ../../example/3_advanced/http_server_cpp20/handle_request.hpp]
[import ../../example/3_advanced/http_server_cpp20/handle_request.cpp]
[import ../../example/3_advanced/http_server_cpp20/server.hpp]
[import ../../example/3_advanced/http_server_cpp20/server.cpp]
[import ../../example/3_advanced/connection_pool/main.cpp]
[import ../../example/3_advanced/connection_pool/types.hpp]
[import ../../example/3_advanced/connection_pool/repository.hpp]
Expand Down
31 changes: 30 additions & 1 deletion doc/qbk/21_examples.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,36 @@ This example assumes you have gone through the [link mysql.examples.setup setup]



[section:connection_pool A REST API server that uses connection pooling]
[section:http_server_cpp20 A REST API server that uses C++20 coroutines]

This example assumes you have gone through the [link mysql.examples.setup setup].

[example_http_server_cpp20_main_cpp]

[example_http_server_cpp20_types_hpp]

[example_http_server_cpp20_error_hpp]

[example_http_server_cpp20_error_cpp]

[example_http_server_cpp20_repository_hpp]

[example_http_server_cpp20_repository_cpp]

[example_http_server_cpp20_handle_request_hpp]

[example_http_server_cpp20_handle_request_cpp]

[example_http_server_cpp20_server_hpp]

[example_http_server_cpp20_server_cpp]

[endsect]




[section:connection_pool A REST API server that uses asio::yield_context]

This example assumes you have gone through the [link mysql.examples.setup setup].

Expand Down
52 changes: 52 additions & 0 deletions example/3_advanced/http_server_cpp20/db_setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--
-- Copyright (c) 2019-2024 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)
--

-- Connection system variables
SET NAMES utf8;

-- Database
DROP DATABASE IF EXISTS boost_mysql_orders;
CREATE DATABASE boost_mysql_orders;
USE boost_mysql_orders;

-- User
DROP USER IF EXISTS 'orders_user'@'%';
CREATE USER 'orders_user'@'%' IDENTIFIED BY 'orders_password';
GRANT ALL PRIVILEGES ON boost_mysql_orders.* TO 'orders_user'@'%';
FLUSH PRIVILEGES;

-- Tables
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
short_name VARCHAR(100) NOT NULL,
descr TEXT,
price INT NOT NULL,
FULLTEXT(short_name, descr)
);

CREATE TABLE orders(
id INT PRIMARY KEY AUTO_INCREMENT,
`status` ENUM('draft', 'pending_payment', 'complete') NOT NULL DEFAULT 'draft'
);

CREATE TABLE order_items(
id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);

-- Contents for the products table
INSERT INTO products (price, short_name, descr) VALUES
(6400, 'A Feast for Odin', 'A Feast for Odin is a points-driven game, with plethora of pathways to victory, with a range of risk balanced against reward. A significant portion of this is your central hall, which has a whopping -86 points of squares and a major part of your game is attempting to cover these up with various tiles. Likewise, long halls and island colonies can also offer large rewards, but they will have penalties of their own.'),
(1600, 'Railroad Ink', 'The critically acclaimed roll and write game where you draw routes on your board trying to connect the exits at its edges. The more you connect, the more points you make, but beware: each incomplete route will make you lose points!'),
(4000, 'Catan', 'Catan is a board game for two to four players in which you compete to gather resources and build the biggest settlements on the fictional island of Catan. It takes approximately one hour to play.'),
(2500, 'Not Alone', 'It is the 25th century. You are a member of an intergalactic expedition shipwrecked on a mysterious planet named Artemia. While waiting for the rescue ship, you begin to explore the planet but an alien entity picks up your scent and begins to hunt you. You are NOT ALONE! Will you survive the dangers of Artemia?'),
(4500, 'Dice Hospital', "In Dice Hospital, a worker placement board game, players are tasked with running a local hospital. Each round you'll be admitting new patients, hiring specialists, building new departments, and treating as many incoming patients as you can.")
;
69 changes: 69 additions & 0 deletions example/3_advanced/http_server_cpp20/error.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright (c) 2019-2024 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)
//

//[example_http_server_cpp20_error_cpp

#include <boost/system/detail/error_category.hpp>

#include <iostream>
#include <mutex>

#include "error.hpp"

namespace {

// Converts an orders::errc to string
const char* error_to_string(orders::errc value)
{
switch (value)
{
case orders::errc::not_found: return "not_found";
case orders::errc::order_invalid_status: return "order_invalid_status";
case orders::errc::product_not_found: return "product_not_found";
default: return "<unknown orders::errc>";
}
}

// The category to be returned by get_orders_category
class orders_category final : public boost::system::error_category
{
public:
// Identifies the error category. Used when converting error_codes to string
const char* name() const noexcept final override { return "orders"; }

// Given a numeric error belonging to this category, convert it to a string
std::string message(int ev) const final override
{
return error_to_string(static_cast<orders::errc>(ev));
}
};

// The error category
static const orders_category g_category;

// The std::mutex that guards std::cerr
static std::mutex g_cerr_mutex;

} // namespace

//
// External interface
//
const boost::system::error_category& orders::get_orders_category() { return g_category; }

std::unique_lock<std::mutex> orders::lock_cerr() { return std::unique_lock{g_cerr_mutex}; }

void orders::log_error(std::string_view header, boost::system::error_code ec)
{
// Lock the mutex
auto guard = lock_cerr();

// Logging the error code prints the number and category. Add the message, too
std::cerr << header << ": " << ec << " " << ec.message() << std::endl;
}

//]
66 changes: 66 additions & 0 deletions example/3_advanced/http_server_cpp20/error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// Copyright (c) 2019-2024 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)
//

#ifndef BOOST_MYSQL_EXAMPLE_3_ADVANCED_HTTP_SERVER_CPP20_ERROR_HPP
#define BOOST_MYSQL_EXAMPLE_3_ADVANCED_HTTP_SERVER_CPP20_ERROR_HPP

//[example_http_server_cpp20_error_hpp
//
// File: error.hpp
//
// Contains an errc enumeration and the required pieces to
// use it with boost::system::error_code.
// We use this indirectly in the DB repository class,
// when using the error codes in boost::system::result.

#include <boost/system/error_category.hpp>

#include <mutex>
#include <string_view>
#include <type_traits>

namespace orders {

// Error code enum for errors originated within our application
enum class errc
{
not_found, // couldn't retrieve or modify a certain resource because it doesn't exist
order_invalid_status, // an operation found an order in a status != the one expected (e.g. not editable)
product_not_found, // a product referenced by a request doesn't exist
};

// To use errc with boost::system::error_code, we need
// to define an error category (see the cpp file).
const boost::system::error_category& get_orders_category();

// Called when constructing an error_code from an errc value.
inline boost::system::error_code make_error_code(errc v)
{
// Roughly, an error_code is an int and a category defining what the int means.
return boost::system::error_code(static_cast<int>(v), get_orders_category());
}

// In multi-threaded programs, using std::cerr without any locking
// can result in interleaved output.
// Locks a mutex guarding std::cerr to prevent this.
// All uses of std::cerr should respect this.
std::unique_lock<std::mutex> lock_cerr();

// A helper function for the common case where we want to log an error code
void log_error(std::string_view header, boost::system::error_code ec);

} // namespace orders

// This specialization is required to construct error_code's from errc values
template <>
struct boost::system::is_error_code_enum<orders::errc> : std::true_type
{
};

//]

#endif
Loading
Loading