void retry_http_call_until_done()

in Source/HTTP/httpcall.cpp [382:477]


void retry_http_call_until_done(
    _In_ HC_UNIQUE_PTR<retry_context> retryContext
    )
{
    auto httpSingleton = get_http_singleton();
    if (nullptr == httpSingleton)
    {
        HC_TRACE_WARNING(HTTPCLIENT, "Http call after HCCleanup was called. Aborting call.");
        XAsyncComplete(retryContext->outerAsyncBlock, E_HC_NOT_INITIALISED, 0);
        return;
    }

    auto requestStartTime = chrono_clock_t::now();
    HC_CALL* call = retryContext->call->get();
    if (call->retryIterationNumber == 0)
    {
        call->firstRequestStartTime = requestStartTime;
    }
    call->retryIterationNumber++;
    if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallPerformExecute [ID %llu] Iteration %d", TO_ULL(call->id), call->retryIterationNumber); }

    if (should_fast_fail(call, requestStartTime, httpSingleton))
    {
        if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallPerformExecute [ID %llu] Fast fail %d", TO_ULL(call->id), call->statusCode); }
        XAsyncComplete(retryContext->outerAsyncBlock, S_OK, 0);
        return;
    }

    auto nestedBlock = http_allocate_unique<XAsyncBlock>();
    if (nestedBlock == nullptr)
    {
        XAsyncComplete(retryContext->outerAsyncBlock, E_OUTOFMEMORY, 0);
        return;
    }

    XTaskQueueHandle nestedQueue = nullptr;
    if (retryContext->outerQueue != nullptr)
    {
        XTaskQueuePortHandle workPort;
        XTaskQueueGetPort(retryContext->outerQueue, XTaskQueuePort::Work, &workPort);
        XTaskQueueCreateComposite(workPort, workPort, &nestedQueue);
    }
    nestedBlock->queue = nestedQueue;
    nestedBlock->context = retryContext.get();
    nestedBlock->callback = [](XAsyncBlock* nestedAsyncBlock)
    {
        HC_UNIQUE_PTR<XAsyncBlock> nestedAsyncPtr{ nestedAsyncBlock };
        HC_UNIQUE_PTR<retry_context> retryContext{ static_cast<retry_context*>(nestedAsyncBlock->context) };

        auto httpSingleton = get_http_singleton();
        if (httpSingleton == nullptr)
        {
            HC_TRACE_WARNING(HTTPCLIENT, "Http completed after HCCleanup was called. Aborting call.");
            XAsyncComplete(retryContext->outerAsyncBlock, E_HC_NOT_INITIALISED, 0);
        }
        else
        {
            auto callStatus = XAsyncGetStatus(nestedAsyncBlock, false);
            auto responseReceivedTime = chrono_clock_t::now();
            uint32_t timeoutWindowInSeconds = 0;
            HC_CALL* call = retryContext->call->get();
            HCHttpCallRequestGetTimeoutWindow(call, &timeoutWindowInSeconds);
            notify_call_routed_handlers(httpSingleton, call);

            if (SUCCEEDED(callStatus) && http_call_should_retry(call, responseReceivedTime))
            {
                if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallPerformExecute [ID %llu] Retry after %lld ms", TO_ULL(call->id), call->delayBeforeRetry.count()); }
                clear_http_call_response(call);
                retry_http_call_until_done(std::move(retryContext));
            }
            else
            {
                XAsyncComplete(retryContext->outerAsyncBlock, callStatus, 0);
            }
        }

        if (nestedAsyncBlock->queue != nullptr)
        {
            XTaskQueueCloseHandle(nestedAsyncBlock->queue);
        }
        // Cleanup with happen when unique ptr's go out of scope
    };

    HRESULT hr = perform_http_call(httpSingleton, call, nestedBlock.get());
    if (SUCCEEDED(hr))
    {
        nestedBlock.release(); // at this point we know do work will be called eventually
        retryContext.release(); // at this point we know do work will be called eventually
    }
    else
    {
        // Cleanup with happen when unique ptr's go out of scope if they weren't released
        XAsyncComplete(retryContext->outerAsyncBlock, hr, 0);
        return;
    }
}