include/boost/corosio/openssl_stream.hpp

100.0% Lines (8/8) 100.0% List of functions (4/4) 100.0% Branches (2/2)
openssl_stream.hpp
f(x) Functions (4)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_OPENSSL_STREAM_HPP
12 #define BOOST_COROSIO_OPENSSL_STREAM_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/tls_context.hpp>
16 #include <boost/corosio/tls_stream.hpp>
17 #include <boost/capy/detail/buffer_array.hpp>
18 #include <boost/capy/concept/stream.hpp>
19 #include <boost/capy/io/any_stream.hpp>
20 #include <boost/capy/io_task.hpp>
21
22 #include <concepts>
23 #include <system_error>
24
25 namespace boost::corosio {
26
27 /** A TLS stream using OpenSSL.
28
29 This class wraps an underlying stream satisfying `capy::Stream`
30 and provides TLS encryption using the OpenSSL library.
31
32 Derives from @ref tls_stream to provide a runtime-polymorphic
33 interface. The TLS operations are implemented as coroutines
34 that orchestrate reads and writes on the underlying stream.
35
36 @par Construction Modes
37
38 Two construction modes are supported:
39
40 - **Owning**: Pass stream by value. The openssl_stream takes
41 ownership and the stream is moved into internal storage.
42
43 - **Reference**: Pass stream by pointer. The openssl_stream
44 does not own the stream; the caller must ensure the stream
45 outlives this object.
46
47 @par Thread Safety
48 Distinct objects: Safe.@n
49 Shared objects: Unsafe.
50
51 @par Example
52 @code
53 tls_context ctx;
54 ctx.set_hostname("example.com");
55 ctx.set_verify_mode(tls_verify_mode::peer);
56
57 corosio::tcp_socket sock(ioc);
58 co_await sock.connect(endpoint);
59
60 // Reference mode - sock must outlive tls
61 corosio::openssl_stream tls(&sock, ctx);
62 auto [ec] = co_await tls.handshake(openssl_stream::client);
63
64 // Or owning mode - tls owns the socket
65 corosio::openssl_stream tls2(std::move(sock), ctx);
66 @endcode
67
68 @see tls_stream, wolfssl_stream
69 */
70 class BOOST_COROSIO_DECL openssl_stream final : public tls_stream
71 {
72 struct impl;
73 capy::any_stream stream_; // must be first - impl_ holds reference
74 impl* impl_;
75
76 public:
77 /** Construct an OpenSSL stream (owning mode).
78
79 Takes ownership of the underlying stream by moving it into
80 internal storage. The stream will be destroyed when this
81 openssl_stream is destroyed.
82
83 @param stream The stream to take ownership of. Must satisfy
84 `capy::Stream`.
85 @param ctx The TLS context containing configuration.
86 */
87 template<capy::Stream S>
88 requires(!std::same_as<std::decay_t<S>, openssl_stream>)
89 openssl_stream(S stream, tls_context const& ctx)
90 : stream_(std::move(stream))
91 , impl_(make_impl(stream_, ctx))
92 {
93 }
94
95 /** Construct an OpenSSL stream (reference mode).
96
97 Wraps the underlying stream without taking ownership. The
98 caller must ensure the stream remains valid for the lifetime
99 of this openssl_stream.
100
101 @param stream Pointer to the stream to wrap. Must satisfy
102 `capy::Stream`.
103 @param ctx The TLS context containing configuration.
104 */
105 template<capy::Stream S>
106 2405x openssl_stream(S* stream, tls_context const& ctx)
107 2405x : stream_(stream)
108
2/2
✓ Branch 3 → 4 taken 2405 times.
✓ Branch 4 → 5 taken 2405 times.
2405x , impl_(make_impl(stream_, ctx))
109 {
110 2405x }
111
112 /** Destructor.
113
114 Releases the underlying OpenSSL resources. If constructed
115 in owning mode, also destroys the underlying stream.
116 */
117 ~openssl_stream() override;
118
119 /** Move construct from another OpenSSL stream.
120
121 @param other The source stream. After the move,
122 @p other is in a valid but unspecified state.
123 */
124 openssl_stream(openssl_stream&& other) noexcept;
125
126 /** Move assign from another OpenSSL stream.
127
128 @param other The source stream. After the move,
129 @p other is in a valid but unspecified state.
130
131 @return `*this`.
132 */
133 openssl_stream& operator=(openssl_stream&& other) noexcept;
134
135 /** Perform the TLS handshake asynchronously.
136
137 Suspends the calling coroutine until the handshake
138 completes, an error occurs, or the operation is
139 cancelled via stop token.
140
141 @par Preconditions
142 The underlying stream must be connected. No other
143 TLS operation may be in progress on this stream.
144
145 @param type The handshake role (client or server).
146
147 @return An awaitable yielding `(error_code)`.
148 */
149 capy::io_task<> handshake(handshake_type type) override;
150
151 /** Shut down the TLS session asynchronously.
152
153 Sends a close_notify alert and waits for the peer's
154 close_notify response. Supports cancellation via
155 stop token.
156
157 @par Preconditions
158 A handshake must have completed successfully. No
159 other TLS operation may be in progress on this stream.
160
161 @return An awaitable yielding `(error_code)`.
162 */
163 capy::io_task<> shutdown() override;
164
165 /** Reset TLS session state for reuse.
166
167 Clears internal buffers and session data so the stream
168 can perform a new handshake on the same underlying
169 connection.
170
171 @par Preconditions
172 No TLS operation may be in progress on this stream.
173 */
174 void reset() override;
175
176 /// Return the underlying stream.
177 1x capy::any_stream& next_layer() noexcept override
178 {
179 1x return stream_;
180 }
181
182 /// Return the underlying stream.
183 1x capy::any_stream const& next_layer() const noexcept override
184 {
185 1x return stream_;
186 }
187
188 /// Return the TLS backend name ("openssl").
189 std::string_view name() const noexcept override;
190
191 protected:
192 capy::io_task<std::size_t> do_read_some(
193 capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers) override;
194
195 capy::io_task<std::size_t> do_write_some(
196 capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) override;
197
198 private:
199 static impl* make_impl(capy::any_stream& stream, tls_context const& ctx);
200 };
201
202 /** Return the error category for raw OpenSSL errors.
203
204 Errors reported by @ref openssl_stream that originate from the OpenSSL
205 error queue (`ERR_get_error`) are assigned this category. Its
206 `message()` decodes the packed OpenSSL error code using OpenSSL's own
207 diagnostic strings, so printing such an `error_code` yields a readable
208 description (for example, "certificate verify failed").
209
210 OpenSSL errors whose library is `ERR_LIB_SYS` are reported with
211 `std::system_category()` instead, since their reason code is a genuine
212 `errno` value.
213
214 @return A reference to a static category object with name
215 `"corosio.openssl"`.
216 */
217 BOOST_COROSIO_DECL std::error_category const&
218 openssl_category() noexcept;
219
220 } // namespace boost::corosio
221
222 #endif
223