@@ -33,20 +33,54 @@ CurlMultiManager::CurlMultiManager(boost::asio::any_io_executor executor)
3333
3434CurlMultiManager::~CurlMultiManager () {
3535 if (multi_handle_) {
36+ // Extract and clear pending handles, callbacks, and headers
37+ std::map<CURL *, CompletionCallback> pending_callbacks;
38+ std::map<CURL *, curl_slist*> pending_headers;
39+ {
40+ std::lock_guard<std::mutex> lock (mutex_);
41+ pending_callbacks = std::move (callbacks_);
42+ pending_headers = std::move (headers_);
43+ callbacks_.clear ();
44+ headers_.clear ();
45+ }
46+
47+ // Remove handles from multi and cleanup resources
48+ // Do NOT invoke callbacks as they may access destroyed objects
49+ for (auto & [easy, callback] : pending_callbacks) {
50+ curl_multi_remove_handle (multi_handle_, easy);
51+
52+ // Free headers if they exist for this handle
53+ auto header_it = pending_headers.find (easy);
54+ if (header_it != pending_headers.end () && header_it->second ) {
55+ curl_slist_free_all (header_it->second );
56+ }
57+
58+ curl_easy_cleanup (easy);
59+ }
60+
3661 curl_multi_cleanup (multi_handle_);
3762 }
3863}
3964
40- void CurlMultiManager::add_handle (CURL * easy, CompletionCallback callback) {
65+ void CurlMultiManager::add_handle (CURL * easy, curl_slist* headers, CompletionCallback callback) {
4166 {
4267 std::lock_guard<std::mutex> lock (mutex_);
4368 callbacks_[easy] = std::move (callback);
69+ headers_[easy] = headers;
4470 }
4571
4672 CURLMcode rc = curl_multi_add_handle (multi_handle_, easy);
4773 if (rc != CURLM_OK ) {
4874 std::lock_guard<std::mutex> lock (mutex_);
4975 callbacks_.erase (easy);
76+
77+ // Free headers on error
78+ auto header_it = headers_.find (easy);
79+ if (header_it != headers_.end () && header_it->second ) {
80+ curl_slist_free_all (header_it->second );
81+ }
82+ headers_.erase (easy);
83+
5084 std::cerr << " Failed to add handle to multi: "
5185 << curl_multi_strerror (rc) << std::endl;
5286 }
@@ -57,6 +91,13 @@ void CurlMultiManager::remove_handle(CURL* easy) {
5791
5892 std::lock_guard<std::mutex> lock (mutex_);
5993 callbacks_.erase (easy);
94+
95+ // Free headers if they exist
96+ auto header_it = headers_.find (easy);
97+ if (header_it != headers_.end () && header_it->second ) {
98+ curl_slist_free_all (header_it->second );
99+ }
100+ headers_.erase (easy);
60101}
61102
62103int CurlMultiManager::socket_callback (CURL * easy, curl_socket_t s, int what,
@@ -144,18 +185,31 @@ void CurlMultiManager::check_multi_info() {
144185 CURLcode result = msg->data .result ;
145186
146187 CompletionCallback callback;
188+ curl_slist* headers = nullptr ;
147189 {
148190 std::lock_guard<std::mutex> lock (mutex_);
149191 auto it = callbacks_.find (easy);
150192 if (it != callbacks_.end ()) {
151193 callback = std::move (it->second );
152194 callbacks_.erase (it);
153195 }
196+
197+ // Get and remove headers
198+ auto header_it = headers_.find (easy);
199+ if (header_it != headers_.end ()) {
200+ headers = header_it->second ;
201+ headers_.erase (header_it);
202+ }
154203 }
155204
156205 // Remove from multi handle
157206 curl_multi_remove_handle (multi_handle_, easy);
158207
208+ // Free headers
209+ if (headers) {
210+ curl_slist_free_all (headers);
211+ }
212+
159213 // Invoke completion callback
160214 if (callback) {
161215 boost::asio::post (executor_, [callback = std::move (callback),
@@ -170,7 +224,7 @@ void CurlMultiManager::check_multi_info() {
170224void CurlMultiManager::start_socket_monitor (SocketInfo* socket_info, int action) {
171225 if (!socket_info->descriptor ) {
172226 // Create descriptor for this socket
173- socket_info->descriptor = std::make_unique <
227+ socket_info->descriptor = std::make_shared <
174228 boost::asio::posix::stream_descriptor>(executor_);
175229 socket_info->descriptor ->assign (socket_info->sockfd );
176230 }
@@ -182,28 +236,72 @@ void CurlMultiManager::start_socket_monitor(SocketInfo* socket_info, int action)
182236
183237 // Monitor for read events
184238 if (action & CURL_POLL_IN ) {
185- socket_info->descriptor ->async_wait (
186- boost::asio::posix::stream_descriptor::wait_read,
187- [weak_self, sockfd](const boost::system::error_code& ec) {
188- if (!ec) {
239+ // Use weak_ptr to safely detect when descriptor is deleted
240+ std::weak_ptr<boost::asio::posix::stream_descriptor> weak_descriptor = socket_info->descriptor ;
241+
242+ // Use shared_ptr for recursive lambda
243+ auto read_handler = std::make_shared<std::function<void ()>>();
244+ *read_handler = [weak_self, sockfd, weak_descriptor, read_handler]() {
245+ // Check if manager and descriptor are still valid
246+ auto self = weak_self.lock ();
247+ auto descriptor = weak_descriptor.lock ();
248+ if (!self || !descriptor) {
249+ return ;
250+ }
251+
252+ descriptor->async_wait (
253+ boost::asio::posix::stream_descriptor::wait_read,
254+ [weak_self, sockfd, weak_descriptor, read_handler](const boost::system::error_code& ec) {
255+ // If operation was canceled or had an error, don't re-register
256+ if (ec) {
257+ return ;
258+ }
259+
189260 if (auto self = weak_self.lock ()) {
190261 self->handle_socket_action (sockfd, CURL_CSELECT_IN );
262+
263+ // Always try to re-register for continuous monitoring
264+ // The validity check at the top of read_handler will stop it if needed
265+ (*read_handler)(); // Recursive call
191266 }
192- }
193- });
267+ });
268+ };
269+ (*read_handler)(); // Initial call
194270 }
195271
196272 // Monitor for write events
197273 if (action & CURL_POLL_OUT ) {
198- socket_info->descriptor ->async_wait (
199- boost::asio::posix::stream_descriptor::wait_write,
200- [weak_self, sockfd](const boost::system::error_code& ec) {
201- if (!ec) {
274+ // Use weak_ptr to safely detect when descriptor is deleted
275+ std::weak_ptr<boost::asio::posix::stream_descriptor> weak_descriptor = socket_info->descriptor ;
276+
277+ // Use shared_ptr for recursive lambda
278+ auto write_handler = std::make_shared<std::function<void ()>>();
279+ *write_handler = [weak_self, sockfd, weak_descriptor, write_handler]() {
280+ // Check if manager and descriptor are still valid
281+ auto self = weak_self.lock ();
282+ auto descriptor = weak_descriptor.lock ();
283+ if (!self || !descriptor) {
284+ return ;
285+ }
286+
287+ descriptor->async_wait (
288+ boost::asio::posix::stream_descriptor::wait_write,
289+ [weak_self, sockfd, weak_descriptor, write_handler](const boost::system::error_code& ec) {
290+ // If operation was canceled or had an error, don't re-register
291+ if (ec) {
292+ return ;
293+ }
294+
202295 if (auto self = weak_self.lock ()) {
203296 self->handle_socket_action (sockfd, CURL_CSELECT_OUT );
297+
298+ // Always try to re-register for continuous monitoring
299+ // The validity check at the top of write_handler will stop it if needed
300+ (*write_handler)(); // Recursive call
204301 }
205- }
206- });
302+ });
303+ };
304+ (*write_handler)(); // Initial call
207305 }
208306}
209307
0 commit comments