void CALLBACK WinHttpConnection::completion_callback()

in Source/HTTP/WinHttp/winhttp_connection.cpp [1002:1155]


void CALLBACK WinHttpConnection::completion_callback(
    HINTERNET hRequestHandle,
    DWORD_PTR context,
    DWORD statusCode,
    _In_ void* statusInfo,
    DWORD statusInfoLength)
{
    // Callback used with WinHTTP to listen for async completions.
    UNREFERENCED_PARAMETER(statusInfoLength);

    WinHttpCallbackContext* callbackContext = reinterpret_cast<WinHttpCallbackContext*>(context);
    WinHttpConnection* pRequestContext = callbackContext->winHttpConnection.get();

    try
    {
        switch (statusCode)
        {
            case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
            {
                callback_status_request_error(hRequestHandle, pRequestContext, statusInfo);
                break;
            }

#if HC_PLATFORM == HC_PLATFORM_GDK
            case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
            {
                callback_status_sending_request(hRequestHandle, pRequestContext, statusInfo);
                break;
            }
#endif // HC_PLATFORM_GDK

            case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
            {
                callback_status_sendrequest_complete(hRequestHandle, pRequestContext, statusInfo);
                break;
            }

            case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
            {
                if (pRequestContext->m_websocketHandle)
                {
                    callback_websocket_status_headers_available(hRequestHandle, callbackContext);
                }
                else
                {
                    callback_status_headers_available(hRequestHandle, pRequestContext, statusInfo);
                }
                break;
            }

            case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
            {
                callback_status_data_available(hRequestHandle, pRequestContext, statusInfo);
                break;
            }

            case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
            {
                if (pRequestContext->m_websocketHandle)
                {
                    callback_websocket_status_read_complete(pRequestContext, statusInfo);
                }
                else
                {
                    callback_status_read_complete(hRequestHandle, pRequestContext, statusInfoLength);
                }
                break;
            }

            case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
            {
                if (pRequestContext->m_websocketHandle)
                {
                    callback_websocket_status_write_complete(pRequestContext);
                }
                else
                {
                    callback_status_write_complete(hRequestHandle, pRequestContext, statusInfo);
                }
                break;
            }

            case WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE:
            {
                if (pRequestContext->m_websocketHandle)
                {
                    assert(pRequestContext->m_winHttpWebSocketExports.queryCloseStatus);

                    USHORT closeReason = 0;
                    DWORD dwReasonLengthConsumed = 0;
                    pRequestContext->m_winHttpWebSocketExports.queryCloseStatus(pRequestContext->m_hRequest, &closeReason, nullptr, 0, &dwReasonLengthConsumed);

                    pRequestContext->on_websocket_disconnected(closeReason);
                }
                break;
            }

            case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
            {
                callback_status_secure_failure(hRequestHandle, pRequestContext, statusInfo);
                break;
            }

            case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
            {
                // For WebSocket, we will also get a notification when the original request handle is closed. We have no action to take in that case
                if (hRequestHandle == pRequestContext->m_hRequest)
                {
                    HC_TRACE_VERBOSE(HTTPCLIENT, "WinHttpConnection [ID %llu] [TID %ul] WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING", TO_ULL(HCHttpCallGetId(pRequestContext->m_call)), GetCurrentThreadId());

                    // WinHttp Shutdown complete. WinHttp guarantees we will get no more callbacks for this request so we can safely cleanup context.
                    // Ensure WinHttpCallbackContext is cleaned before invoking callback in case this is happening during HCCleanup
                    HC_UNIQUE_PTR<WinHttpCallbackContext> reclaim{ callbackContext };

                    ConnectionClosedCallback connectionClosedCallback{};
                    {
                        win32_cs_autolock cs{ &pRequestContext->m_lock };
                        pRequestContext->m_hRequest = nullptr;
                        connectionClosedCallback = std::move(pRequestContext->m_connectionClosedCallback);
                        pRequestContext->m_state = ConnectionState::Closed;
                    }
                    reclaim.reset();

                    if (connectionClosedCallback)
                    {
                        connectionClosedCallback();
                    }
                }
                break;
            }

            default:
            {
                HC_TRACE_VERBOSE(HTTPCLIENT, "WinHttpConnection WinHttp callback statusCode=%ul", statusCode);
                break;
            }
        }
    }
    catch (std::bad_alloc const& e)
    {
        HC_TRACE_ERROR(HTTPCLIENT, "WinHttpConnection [%d] std::bad_alloc in completion_callback: %s", E_OUTOFMEMORY, e.what());
        pRequestContext->complete_task(E_OUTOFMEMORY);
    }
    catch (std::exception const& e)
    {
        HC_TRACE_ERROR(HTTPCLIENT, "WinHttpConnection [%d] std::exception in completion_callback: %s", E_FAIL, e.what());
        pRequestContext->complete_task(E_FAIL);
    }
    catch (...)
    {
        HC_TRACE_ERROR(HTTPCLIENT, "WinHttpConnection [%d] unknown exception in completion_callback", E_FAIL);
        pRequestContext->complete_task(E_FAIL);
    }
}