@@ -151,15 +151,23 @@ struct curlFileTransfer : public FileTransfer
151151 }
152152 }
153153
154- void failEx (std::exception_ptr ex)
154+ void failEx (std::exception_ptr ex) noexcept
155155 {
156156 assert (!done);
157157 done = true ;
158+ try {
159+ std::rethrow_exception (ex);
160+ } catch (nix::Error & e) {
161+ /* Add more context to the error message. */
162+ e.addTrace ({}, " during %s of '%s'" , Uncolored (request.verb ()), request.uri .to_string ());
163+ } catch (...) {
164+ /* Can't add more context to the error. */
165+ }
158166 callback.rethrow (ex);
159167 }
160168
161169 template <class T >
162- void fail (T && e)
170+ void fail (T && e) noexcept
163171 {
164172 failEx (std::make_exception_ptr (std::forward<T>(e)));
165173 }
@@ -168,32 +176,30 @@ struct curlFileTransfer : public FileTransfer
168176 std::shared_ptr<FinishSink> decompressionSink;
169177 std::optional<StringSink> errorSink;
170178
171- std::exception_ptr writeException ;
179+ std::exception_ptr callbackException ;
172180
173- size_t writeCallback (void * contents, size_t size, size_t nmemb)
174- {
175- try {
176- size_t realSize = size * nmemb;
177- result.bodySize += realSize;
178-
179- if (!decompressionSink) {
180- decompressionSink = makeDecompressionSink (encoding, finalSink);
181- if (!successfulStatuses.count (getHTTPStatus ())) {
182- // In this case we want to construct a TeeSink, to keep
183- // the response around (which we figure won't be big
184- // like an actual download should be) to improve error
185- // messages.
186- errorSink = StringSink{};
187- }
181+ size_t writeCallback (void * contents, size_t size, size_t nmemb) noexcept
182+ try {
183+ size_t realSize = size * nmemb;
184+ result.bodySize += realSize;
185+
186+ if (!decompressionSink) {
187+ decompressionSink = makeDecompressionSink (encoding, finalSink);
188+ if (!successfulStatuses.count (getHTTPStatus ())) {
189+ // In this case we want to construct a TeeSink, to keep
190+ // the response around (which we figure won't be big
191+ // like an actual download should be) to improve error
192+ // messages.
193+ errorSink = StringSink{};
188194 }
195+ }
189196
190- (*decompressionSink)({(char *) contents, realSize});
197+ (*decompressionSink)({(char *) contents, realSize});
191198
192- return realSize;
193- } catch (...) {
194- writeException = std::current_exception ();
195- return 0 ;
196- }
199+ return realSize;
200+ } catch (...) {
201+ callbackException = std::current_exception ();
202+ return 0 ;
197203 }
198204
199205 static size_t writeCallbackWrapper (void * contents, size_t size, size_t nmemb, void * userp)
@@ -209,8 +215,8 @@ struct curlFileTransfer : public FileTransfer
209215 result.urls .push_back (effectiveUriCStr);
210216 }
211217
212- size_t headerCallback (void * contents, size_t size, size_t nmemb)
213- {
218+ size_t headerCallback (void * contents, size_t size, size_t nmemb) noexcept
219+ try {
214220 size_t realSize = size * nmemb;
215221 std::string line ((char *) contents, realSize);
216222 printMsg (lvlVomit, " got header for '%s': %s" , request.uri , trim (line));
@@ -263,21 +269,33 @@ struct curlFileTransfer : public FileTransfer
263269 }
264270 }
265271 return realSize;
272+ } catch (...) {
273+ #if LIBCURL_VERSION_NUM >= 0x075700
274+ /* https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html:
275+ You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. */
276+ callbackException = std::current_exception ();
277+ return CURL_WRITEFUNC_ERROR;
278+ #else
279+ return realSize;
280+ #endif
266281 }
267282
268283 static size_t headerCallbackWrapper (void * contents, size_t size, size_t nmemb, void * userp)
269284 {
270285 return ((TransferItem *) userp)->headerCallback (contents, size, nmemb);
271286 }
272287
273- int progressCallback (curl_off_t dltotal, curl_off_t dlnow)
274- {
275- try {
276- act.progress (dlnow, dltotal);
277- } catch (nix::Interrupted &) {
278- assert (getInterrupted ());
279- }
288+ int progressCallback (curl_off_t dltotal, curl_off_t dlnow) noexcept
289+ try {
290+ act.progress (dlnow, dltotal);
280291 return getInterrupted ();
292+ } catch (nix::Interrupted &) {
293+ assert (getInterrupted ());
294+ return 1 ;
295+ } catch (...) {
296+ /* Something unexpected has happened like logger throwing an exception. */
297+ callbackException = std::current_exception ();
298+ return 1 ;
281299 }
282300
283301 static int progressCallbackWrapper (
@@ -288,11 +306,14 @@ struct curlFileTransfer : public FileTransfer
288306 return item.progressCallback (isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow);
289307 }
290308
291- static int debugCallback (CURL * handle, curl_infotype type, char * data, size_t size, void * userptr)
292- {
309+ static int debugCallback (CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) noexcept
310+ try {
293311 if (type == CURLINFO_TEXT)
294312 vomit (" curl: %s" , chomp (std::string (data, size)));
295313 return 0 ;
314+ } catch (...) {
315+ /* Swallow the exception. Nothing left to do. */
316+ return 0 ;
296317 }
297318
298319 size_t readCallback (char * buffer, size_t size, size_t nitems) noexcept
@@ -302,6 +323,7 @@ struct curlFileTransfer : public FileTransfer
302323 } catch (EndOfFile &) {
303324 return 0 ;
304325 } catch (...) {
326+ callbackException = std::current_exception ();
305327 return CURL_READFUNC_ABORT;
306328 }
307329
@@ -333,6 +355,7 @@ struct curlFileTransfer : public FileTransfer
333355 }
334356 return CURL_SEEKFUNC_OK;
335357 } catch (...) {
358+ callbackException = std::current_exception ();
336359 return CURL_SEEKFUNC_FAIL;
337360 }
338361
@@ -476,7 +499,7 @@ struct curlFileTransfer : public FileTransfer
476499 try {
477500 decompressionSink->finish ();
478501 } catch (...) {
479- writeException = std::current_exception ();
502+ callbackException = std::current_exception ();
480503 }
481504 }
482505
@@ -485,8 +508,8 @@ struct curlFileTransfer : public FileTransfer
485508 httpStatus = 304 ;
486509 }
487510
488- if (writeException )
489- failEx (writeException );
511+ if (callbackException )
512+ failEx (callbackException );
490513
491514 else if (code == CURLE_OK && successfulStatuses.count (httpStatus)) {
492515 result.cached = httpStatus == 304 ;
0 commit comments