diff --git a/ddnet-libs b/ddnet-libs index 4d796ea11..a4fbe0e52 160000 --- a/ddnet-libs +++ b/ddnet-libs @@ -1 +1 @@ -Subproject commit 4d796ea119b52c8901286e14ab96faf7353a5d59 +Subproject commit a4fbe0e52338a129bc3ac2e3a1de54de1d175279 diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp index c049741a0..c1ab63e83 100644 --- a/src/engine/server/register.cpp +++ b/src/engine/server/register.cpp @@ -21,6 +21,7 @@ class CRegister : public IRegister STATUS_OK, STATUS_NEEDCHALLENGE, STATUS_NEEDINFO, + STATUS_ERROR, PROTOCOL_TW6_IPV6 = 0, PROTOCOL_TW6_IPV4, @@ -156,6 +157,10 @@ bool CRegister::StatusFromString(int *pResult, const char *pString) { *pResult = STATUS_NEEDINFO; } + else if(str_comp(pString, "error") == 0) + { + *pResult = STATUS_ERROR; + } else { *pResult = -1; @@ -302,6 +307,7 @@ void CRegister::CProtocol::SendRegister() } pRegister->LogProgress(HTTPLOG::FAILURE); pRegister->IpResolve(ProtocolToIpresolve(m_Protocol)); + pRegister->FailOnErrorStatus(false); int RequestIndex; { @@ -413,9 +419,8 @@ void CRegister::CProtocol::CJob::Run() m_pRegister->Wait(); if(m_pRegister->State() != EHttpState::DONE) { - // TODO: log the error response content from master // TODO: exponential backoff - log_error(ProtocolToSystem(m_Protocol), "error response from master"); + log_error(ProtocolToSystem(m_Protocol), "error sending request to master"); return; } json_value *pJson = m_pRegister->ResultJson(); @@ -439,6 +444,25 @@ void CRegister::CProtocol::CJob::Run() json_value_free(pJson); return; } + if(Status == STATUS_ERROR) + { + const json_value &Message = Json["message"]; + if(Message.type != json_string) + { + json_value_free(pJson); + log_error(ProtocolToSystem(m_Protocol), "invalid JSON error response from master"); + return; + } + log_error(ProtocolToSystem(m_Protocol), "error response from master: %d: %s", m_pRegister->StatusCode(), (const char *)Message); + json_value_free(pJson); + return; + } + if(m_pRegister->StatusCode() >= 400) + { + log_error(ProtocolToSystem(m_Protocol), "non-success status code %d from master without error code", m_pRegister->StatusCode()); + json_value_free(pJson); + return; + } { CLockScope ls(m_pShared->m_Lock); if(Status != STATUS_OK || Status != m_pShared->m_LatestResponseStatus) diff --git a/src/engine/shared/http.cpp b/src/engine/shared/http.cpp index 695fc2deb..af0fb6a27 100644 --- a/src/engine/shared/http.cpp +++ b/src/engine/shared/http.cpp @@ -139,7 +139,10 @@ bool CHttpRequest::ConfigureHandle(void *pHandle) #endif curl_easy_setopt(pH, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(pH, CURLOPT_MAXREDIRS, 4L); - curl_easy_setopt(pH, CURLOPT_FAILONERROR, 1L); + if(m_FailOnErrorStatus) + { + curl_easy_setopt(pH, CURLOPT_FAILONERROR, 1L); + } curl_easy_setopt(pH, CURLOPT_URL, m_aUrl); curl_easy_setopt(pH, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(pH, CURLOPT_USERAGENT, GAME_NAME " " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")"); @@ -256,8 +259,16 @@ int CHttpRequest::ProgressCallback(void *pUser, double DlTotal, double DlCurr, d return pTask->m_Abort ? -1 : 0; } -void CHttpRequest::OnCompletionInternal(unsigned int Result) +void CHttpRequest::OnCompletionInternal(void *pHandle, unsigned int Result) { + if(pHandle) + { + CURL *pH = (CURL *)pHandle; + long StatusCode; + curl_easy_getinfo(pH, CURLINFO_RESPONSE_CODE, &StatusCode); + m_StatusCode = StatusCode; + } + EHttpState State; const CURLcode Code = static_cast(Result); if(Code != CURLE_OK) @@ -373,6 +384,12 @@ const SHA256_DIGEST &CHttpRequest::ResultSha256() const return m_ActualSha256; } +int CHttpRequest::StatusCode() const +{ + dbg_assert(State() == EHttpState::DONE, "Request not done"); + return m_StatusCode; +} + bool CHttp::Init(std::chrono::milliseconds ShutdownDelay) { m_ShutdownDelay = ShutdownDelay; @@ -478,7 +495,7 @@ void CHttp::RunLoop() auto pRequest = std::move(RequestIt->second); m_RunningRequests.erase(RequestIt); - pRequest->OnCompletionInternal(pMsg->data.result); + pRequest->OnCompletionInternal(pMsg->easy_handle, pMsg->data.result); curl_multi_remove_handle(m_pMultiH, pMsg->easy_handle); curl_easy_cleanup(pMsg->easy_handle); } @@ -534,20 +551,21 @@ void CHttp::RunLoop() for(auto &pRequest : m_PendingRequests) { str_copy(pRequest->m_aErr, "Shutting down"); - pRequest->OnCompletionInternal(CURLE_ABORTED_BY_CALLBACK); + pRequest->OnCompletionInternal(nullptr, CURLE_ABORTED_BY_CALLBACK); } for(auto &ReqPair : m_RunningRequests) { auto &[pHandle, pRequest] = ReqPair; + + str_copy(pRequest->m_aErr, "Shutting down"); + pRequest->OnCompletionInternal(pHandle, CURLE_ABORTED_BY_CALLBACK); + if(Cleanup) { curl_multi_remove_handle(m_pMultiH, pHandle); curl_easy_cleanup(pHandle); } - - str_copy(pRequest->m_aErr, "Shutting down"); - pRequest->OnCompletionInternal(CURLE_ABORTED_BY_CALLBACK); } if(Cleanup) @@ -564,7 +582,7 @@ void CHttp::Run(std::shared_ptr pRequest) if(m_Shutdown) { str_copy(pRequestImpl->m_aErr, "Shutting down"); - pRequestImpl->OnCompletionInternal(CURLE_ABORTED_BY_CALLBACK); + pRequestImpl->OnCompletionInternal(nullptr, CURLE_ABORTED_BY_CALLBACK); return; } m_Cv.wait(Lock, [this]() { return m_State != CHttp::UNINITIALIZED; }); diff --git a/src/engine/shared/http.h b/src/engine/shared/http.h index 27875f09a..f8d874f3e 100644 --- a/src/engine/shared/http.h +++ b/src/engine/shared/http.h @@ -111,14 +111,19 @@ class CHttpRequest : public IHttpRequest HTTPLOG m_LogProgress = HTTPLOG::ALL; IPRESOLVE m_IpResolve = IPRESOLVE::WHATEVER; + bool m_FailOnErrorStatus = true; + char m_aErr[256]; // 256 == CURL_ERROR_SIZE std::atomic m_State{EHttpState::QUEUED}; std::atomic m_Abort{false}; + int m_StatusCode = 0; + // Abort the request with an error if `BeforeInit()` returns false. bool BeforeInit(); bool ConfigureHandle(void *pHandle); // void * == CURL * - void OnCompletionInternal(unsigned int Result); // unsigned int == CURLcode + // `pHandle` can be nullptr if no handle was ever created for this request. + void OnCompletionInternal(void *pHandle, unsigned int Result); // void * == CURL *, unsigned int == CURLcode // Abort the request if `OnData()` returns something other than // `DataSize`. @@ -140,6 +145,7 @@ public: void MaxResponseSize(int64_t MaxResponseSize) { m_MaxResponseSize = MaxResponseSize; } void LogProgress(HTTPLOG LogProgress) { m_LogProgress = LogProgress; } void IpResolve(IPRESOLVE IpResolve) { m_IpResolve = IpResolve; } + void FailOnErrorStatus(bool FailOnErrorStatus) { m_FailOnErrorStatus = FailOnErrorStatus; } void WriteToFile(IStorage *pStorage, const char *pDest, int StorageType); void ExpectSha256(const SHA256_DIGEST &Sha256) { m_ExpectedSha256 = Sha256; } void Head() { m_Type = REQUEST::HEAD; } @@ -198,8 +204,9 @@ public: void Result(unsigned char **ppResult, size_t *pResultLength) const; json_value *ResultJson() const; - const SHA256_DIGEST &ResultSha256() const; + + int StatusCode() const; }; inline std::unique_ptr HttpHead(const char *pUrl)