1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-22 02:09:36 +01:00

Merge pull request #14591 from NixOS/filetransfer-error-handling

libstore/filetransfer: Improve error handling
This commit is contained in:
John Ericson 2025-11-19 01:38:17 +00:00 committed by GitHub
commit dfac44cdfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -151,15 +151,23 @@ struct curlFileTransfer : public FileTransfer
} }
} }
void failEx(std::exception_ptr ex) void failEx(std::exception_ptr ex) noexcept
{ {
assert(!done); assert(!done);
done = true; done = true;
try {
std::rethrow_exception(ex);
} catch (nix::Error & e) {
/* Add more context to the error message. */
e.addTrace({}, "during %s of '%s'", Uncolored(request.verb()), request.uri.to_string());
} catch (...) {
/* Can't add more context to the error. */
}
callback.rethrow(ex); callback.rethrow(ex);
} }
template<class T> template<class T>
void fail(T && e) void fail(T && e) noexcept
{ {
failEx(std::make_exception_ptr(std::forward<T>(e))); failEx(std::make_exception_ptr(std::forward<T>(e)));
} }
@ -168,32 +176,30 @@ struct curlFileTransfer : public FileTransfer
std::shared_ptr<FinishSink> decompressionSink; std::shared_ptr<FinishSink> decompressionSink;
std::optional<StringSink> errorSink; std::optional<StringSink> errorSink;
std::exception_ptr writeException; std::exception_ptr callbackException;
size_t writeCallback(void * contents, size_t size, size_t nmemb) size_t writeCallback(void * contents, size_t size, size_t nmemb) noexcept
{ try {
try { size_t realSize = size * nmemb;
size_t realSize = size * nmemb; result.bodySize += realSize;
result.bodySize += realSize;
if (!decompressionSink) { if (!decompressionSink) {
decompressionSink = makeDecompressionSink(encoding, finalSink); decompressionSink = makeDecompressionSink(encoding, finalSink);
if (!successfulStatuses.count(getHTTPStatus())) { if (!successfulStatuses.count(getHTTPStatus())) {
// In this case we want to construct a TeeSink, to keep // In this case we want to construct a TeeSink, to keep
// the response around (which we figure won't be big // the response around (which we figure won't be big
// like an actual download should be) to improve error // like an actual download should be) to improve error
// messages. // messages.
errorSink = StringSink{}; errorSink = StringSink{};
}
} }
(*decompressionSink)({(char *) contents, realSize});
return realSize;
} catch (...) {
writeException = std::current_exception();
return 0;
} }
(*decompressionSink)({(char *) contents, realSize});
return realSize;
} catch (...) {
callbackException = std::current_exception();
return 0;
} }
static size_t writeCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) static size_t writeCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp)
@ -209,8 +215,8 @@ struct curlFileTransfer : public FileTransfer
result.urls.push_back(effectiveUriCStr); result.urls.push_back(effectiveUriCStr);
} }
size_t headerCallback(void * contents, size_t size, size_t nmemb) size_t headerCallback(void * contents, size_t size, size_t nmemb) noexcept
{ try {
size_t realSize = size * nmemb; size_t realSize = size * nmemb;
std::string line((char *) contents, realSize); std::string line((char *) contents, realSize);
printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line)); printMsg(lvlVomit, "got header for '%s': %s", request.uri, trim(line));
@ -263,6 +269,15 @@ struct curlFileTransfer : public FileTransfer
} }
} }
return realSize; return realSize;
} catch (...) {
#if LIBCURL_VERSION_NUM >= 0x075700
/* https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html:
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. */
callbackException = std::current_exception();
return CURL_WRITEFUNC_ERROR;
#else
return realSize;
#endif
} }
static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp)
@ -270,14 +285,17 @@ struct curlFileTransfer : public FileTransfer
return ((TransferItem *) userp)->headerCallback(contents, size, nmemb); return ((TransferItem *) userp)->headerCallback(contents, size, nmemb);
} }
int progressCallback(curl_off_t dltotal, curl_off_t dlnow) int progressCallback(curl_off_t dltotal, curl_off_t dlnow) noexcept
{ try {
try { act.progress(dlnow, dltotal);
act.progress(dlnow, dltotal);
} catch (nix::Interrupted &) {
assert(getInterrupted());
}
return getInterrupted(); return getInterrupted();
} catch (nix::Interrupted &) {
assert(getInterrupted());
return 1;
} catch (...) {
/* Something unexpected has happened like logger throwing an exception. */
callbackException = std::current_exception();
return 1;
} }
static int progressCallbackWrapper( static int progressCallbackWrapper(
@ -288,11 +306,14 @@ struct curlFileTransfer : public FileTransfer
return item.progressCallback(isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow); return item.progressCallback(isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow);
} }
static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) noexcept
{ try {
if (type == CURLINFO_TEXT) if (type == CURLINFO_TEXT)
vomit("curl: %s", chomp(std::string(data, size))); vomit("curl: %s", chomp(std::string(data, size)));
return 0; return 0;
} catch (...) {
/* Swallow the exception. Nothing left to do. */
return 0;
} }
size_t readCallback(char * buffer, size_t size, size_t nitems) noexcept size_t readCallback(char * buffer, size_t size, size_t nitems) noexcept
@ -302,6 +323,7 @@ struct curlFileTransfer : public FileTransfer
} catch (EndOfFile &) { } catch (EndOfFile &) {
return 0; return 0;
} catch (...) { } catch (...) {
callbackException = std::current_exception();
return CURL_READFUNC_ABORT; return CURL_READFUNC_ABORT;
} }
@ -333,6 +355,7 @@ struct curlFileTransfer : public FileTransfer
} }
return CURL_SEEKFUNC_OK; return CURL_SEEKFUNC_OK;
} catch (...) { } catch (...) {
callbackException = std::current_exception();
return CURL_SEEKFUNC_FAIL; return CURL_SEEKFUNC_FAIL;
} }
@ -476,7 +499,7 @@ struct curlFileTransfer : public FileTransfer
try { try {
decompressionSink->finish(); decompressionSink->finish();
} catch (...) { } catch (...) {
writeException = std::current_exception(); callbackException = std::current_exception();
} }
} }
@ -485,8 +508,8 @@ struct curlFileTransfer : public FileTransfer
httpStatus = 304; httpStatus = 304;
} }
if (writeException) if (callbackException)
failEx(writeException); failEx(callbackException);
else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) { else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) {
result.cached = httpStatus == 304; result.cached = httpStatus == 304;