summaryrefslogtreecommitdiff
path: root/indra/newview/llpanelprofile.cpp
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-07-01 22:53:16 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-07-03 16:15:04 +0300
commitf2c356fc7130ce5e38b994dcfa1e210609b458b9 (patch)
treea93da65897eaea9c09b4c3f2c1d07b3de8a0d861 /indra/newview/llpanelprofile.cpp
parentc99cd251383ed17b4dd1210ee0701d2a3b4dd0e5 (diff)
#4094 Handle two stage uploads more reliably
Diffstat (limited to 'indra/newview/llpanelprofile.cpp')
-rw-r--r--indra/newview/llpanelprofile.cpp158
1 files changed, 106 insertions, 52 deletions
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 34d2d4d6a5..b33696244d 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -113,77 +113,131 @@ LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::stri
LLCore::HttpOptions::ptr_t httpOpts = std::make_shared<LLCore::HttpOptions>();
httpOpts->setFollowRedirects(true);
- LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
+ // Retry stage-2 upload by re-requesting a fresh one-time uploader capability (up to 3 attempts total)
+ const S32 MAX_UPLOAD_RETRIES = 2;
+ S32 upload_retry_count = 0;
+ LLUUID result_uuid;
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- if (!status)
- {
- // todo: notification?
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
- return LLUUID::null;
- }
- if (!result.has("uploader"))
+ while (upload_retry_count <= MAX_UPLOAD_RETRIES)
{
- // todo: notification?
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
- return LLUUID::null;
- }
- std::string uploader_cap = result["uploader"].asString();
- if (uploader_cap.empty())
- {
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
- return LLUUID::null;
- }
+ // Stage 1: Request uploader URL
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
- // Upload the image
- LLCore::HttpRequest::ptr_t uploaderhttpRequest = std::make_shared<LLCore::HttpRequest>();
- LLCore::HttpHeaders::ptr_t uploaderhttpHeaders = std::make_shared<LLCore::HttpHeaders>();
- LLCore::HttpOptions::ptr_t uploaderhttpOpts = std::make_shared<LLCore::HttpOptions>();
- S64 length;
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- {
- llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
- if (!instream.is_open())
+ if (!status)
{
- LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
+ return LLUUID::null;
+ }
+
+ if (!result.has("uploader"))
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
return LLUUID::null;
}
- length = instream.tellg();
- }
- uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
- uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
- uploaderhttpOpts->setFollowRedirects(true);
+ std::string uploader_cap = result["uploader"].asString();
+ if (uploader_cap.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
+ return LLUUID::null;
+ }
+
+ // Stage 2: Upload the image
+ LLCore::HttpRequest::ptr_t uploaderhttpRequest = std::make_shared<LLCore::HttpRequest>();
+ LLCore::HttpHeaders::ptr_t uploaderhttpHeaders = std::make_shared<LLCore::HttpHeaders>();
+ LLCore::HttpOptions::ptr_t uploaderhttpOpts = std::make_shared<LLCore::HttpOptions>();
+ S64 length;
+
+ {
+ llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
+ if (!instream.is_open())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
+ return LLUUID::null;
+ }
+ length = instream.tellg();
+ }
- result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2");
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, std::to_string(length));
+ uploaderhttpOpts->setFollowRedirects(true);
- httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+ result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
- LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- if (!status)
- {
- LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL;
- return LLUUID::null;
- }
+ LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
- if (result["state"].asString() != "complete")
- {
- if (result.has("message"))
+ if (!status)
{
- LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
+ if (upload_retry_count < MAX_UPLOAD_RETRIES)
+ {
+ upload_retry_count++;
+ LL_WARNS("AvatarProperties") << "Failed to upload image (attempt " << upload_retry_count
+ << " of " << (MAX_UPLOAD_RETRIES + 1) << "): " << status.toString()
+ << ", re-requesting uploader..." << LL_ENDL;
+ llcoro::suspendUntilTimeout(1.0f);
+ continue;
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image after " << (MAX_UPLOAD_RETRIES + 1)
+ << " attempts: " << status.toString() << LL_ENDL;
+ return LLUUID::null;
+ }
}
- else
+
+ // Todo: should we really repeat if 'complete' not set?
+ if (result["state"].asString() != "complete")
{
- LL_WARNS("AvatarProperties") << "Failed to upload image " << result << LL_ENDL;
+ if (upload_retry_count < MAX_UPLOAD_RETRIES)
+ {
+ upload_retry_count++;
+ if (result.has("message"))
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"]
+ << " message: " << result["message"] << " (attempt "
+ << upload_retry_count << " of " << (MAX_UPLOAD_RETRIES + 1)
+ << "), re-requesting uploader..." << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image (attempt " << upload_retry_count
+ << " of " << (MAX_UPLOAD_RETRIES + 1)
+ << "), re-requesting uploader..." << LL_ENDL;
+ }
+ llcoro::suspendUntilTimeout(1.0f);
+ continue;
+ }
+ else
+ {
+ if (result.has("message"))
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image after " << (MAX_UPLOAD_RETRIES + 1)
+ << " attempts, state " << result["state"]
+ << " message: " << result["message"] << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image after " << (MAX_UPLOAD_RETRIES + 1)
+ << " attempts" << LL_ENDL;
+ }
+ return LLUUID::null;
+ }
}
- return LLUUID::null;
+
+ // Success!
+ result_uuid = result["new_asset"].asUUID();
+ break;
}
- return result["new_asset"].asUUID();
+ return result_uuid;
}
enum EProfileImageType