File: client.cpp

package info (click to toggle)
boost1.90 1.90.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 593,120 kB
  • sloc: cpp: 4,190,908; xml: 196,648; python: 34,618; ansic: 23,145; asm: 5,468; sh: 3,774; makefile: 1,161; perl: 1,020; sql: 728; ruby: 676; yacc: 478; java: 77; lisp: 24; csh: 6
file content (137 lines) | stat: -rw-r--r-- 3,799 bytes parent folder | download | duplicates (5)
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
//
// Copyright (c) 2025 Mohammad Nejati
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>

#include <iostream>

#if defined(BOOST_ASIO_HAS_CO_AWAIT)

namespace beast = boost::beast;
namespace http  = beast::http;
namespace net   = boost::asio;
namespace ssl   = net::ssl;

void
print_exception(std::exception_ptr eptr)
{
    if(eptr)
    {
        try
        {
            std::rethrow_exception(eptr);
        }
        catch(std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }
    }
}

net::awaitable<void>
request(ssl::context& ctx)
{
    auto executor = co_await net::this_coro::executor;
    net::ip::tcp::endpoint endpoint{ {}, 8080 };
    ssl::stream<net::ip::tcp::socket> stream{ executor, ctx };

    // Connect TCP socket
    co_await stream.lowest_layer().async_connect(endpoint);

    // Set Server Name Indication (SNI)
    if(!SSL_set_tlsext_host_name(stream.native_handle(), "localhost"))
    {
        throw beast::system_error(
            static_cast<int>(::ERR_get_error()),
            net::error::get_ssl_category());
    }

    // Set a callback to verify that the hostname in the server
    // certificate matches the expected value
    stream.set_verify_callback(ssl::host_name_verification("localhost"));

    // Perform the SSL handshake
    co_await stream.async_handshake(ssl::stream_base::client);

    // Write an HTTP GET request
    http::request<http::empty_body> req{ http::verb::get, "/", 11 };
    req.set(http::field::host, "localhost");
    req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
    co_await http::async_write(stream, req);

    // Read the response
    beast::flat_buffer buf;
    http::response<http::string_body> res;
    co_await http::async_read(stream, buf, res);

    // Print the response body
    std::cout << res.body();

    // Gracefully shutdown the SSL stream
    auto [ec] = co_await stream.async_shutdown(net::as_tuple);
    if(ec && ec != ssl::error::stream_truncated)
        throw boost::system::system_error(ec);
}

int
main()
{
    try
    {
        // The io_context is required for all I/O
        net::io_context ioc;

        // The SSL context is required, and holds certificates,
        // configurations and session related data
        ssl::context ctx{ ssl::context::sslv23 };

        // https://docs.openssl.org/3.4/man3/SSL_CTX_set_options/
        ctx.set_options(
            ssl::context::no_sslv2 | ssl::context::default_workarounds |
            ssl::context::single_dh_use);

        // set up the peer verification mode so that the TLS/SSL handshake fails
        // if the certificate verification is unsuccessful
        ctx.set_verify_mode(ssl::verify_peer);

        // The servers's certificate will be verified against this
        // certificate authority.
        ctx.load_verify_file("ca.crt");

        // In a real application, the passphrase would be read from
        // a secure place, such as a key vault.
        ctx.set_password_callback([](auto, auto) { return "123456"; });

        // Client certificate and private key (if server request for).
        ctx.use_certificate_chain_file("client.crt");
        ctx.use_private_key_file("client.key", ssl::context::pem);

        net::co_spawn(ioc, request(ctx), print_exception);

        ioc.run();
    }
    catch(std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

#else

int
main(int, char*[])
{
    std::printf("awaitables require C++20\n");
    return EXIT_FAILURE;
}

#endif