include/boost/corosio/openssl_stream.hpp

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