Skip to content

Commit dfac44c

Browse files
authored
Merge pull request #14591 from NixOS/filetransfer-error-handling
libstore/filetransfer: Improve error handling
2 parents f4989b1 + 36f4e29 commit dfac44c

File tree

1 file changed

+61
-38
lines changed

1 file changed

+61
-38
lines changed

src/libstore/filetransfer.cc

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)