in Source/WebSocket/hcwebsocket.cpp [37:166]
HRESULT HC_WEBSOCKET::Connect(
_In_z_ const char* uri,
_In_z_ const char* subProtocol,
_Inout_ XAsyncBlock* asyncBlock
) noexcept
{
auto httpSingleton = get_http_singleton();
if (!httpSingleton)
{
return E_HC_NOT_INITIALISED;
}
if (m_state != State::Initial)
{
return E_UNEXPECTED;
}
m_uri = uri;
m_subProtocol = subProtocol;
WebSocketPerformInfo const& info = httpSingleton->m_websocketPerform;
auto connectFunc = info.connect;
if (connectFunc != nullptr)
{
try
{
// Trap the result of the connect before returning to client. Otherwise we have no way of knowing
// if we are in a connected state. This also helps us handle proper disconnection if we were connecting,
// when the client closed their handle.
ZeroMemory(&m_connectAsyncBlock, sizeof(XAsyncBlock));
m_connectAsyncBlock.queue = asyncBlock->queue;
m_connectAsyncBlock.context = this;
m_connectAsyncBlock.callback = [](XAsyncBlock* async)
{
auto thisPtr{ static_cast<HC_WEBSOCKET*>(async->context) };
HRESULT hr = HCGetWebSocketConnectResult(async, &thisPtr->m_connectResult);
// We auto disconnect if the client has already closed all handles.
bool doDisconnect{ false };
// We release the provider's reference if we don't expect any more callbacks from the WebSocket provider.
bool doDecRef { false };
{
std::lock_guard<std::recursive_mutex> lock{ thisPtr->m_mutex };
if (thisPtr->m_state != State::Connecting)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr) && SUCCEEDED(thisPtr->m_connectResult.errorCode))
{
if (thisPtr->m_clientRefCount > 0)
{
thisPtr->m_state = State::Connected;
}
else
{
doDisconnect = true;
}
}
else
{
HC_TRACE_INFORMATION(WEBSOCKET, "Websocket connection attempt failed");
// Release providers ref if connect fails. We do not expect a close event in this case.
thisPtr->m_state = State::Disconnected;
doDecRef = true;
}
}
if (doDisconnect)
{
thisPtr->Disconnect();
}
if (doDecRef)
{
thisPtr->DecRef();
}
XAsyncComplete(thisPtr->m_clientConnectAsyncBlock, hr, sizeof(WebSocketCompletionResult));
};
m_clientConnectAsyncBlock = asyncBlock;
XAsyncBegin(m_clientConnectAsyncBlock, this, (void*)HCWebSocketConnectAsync, __FUNCTION__,
[](XAsyncOp op, const XAsyncProviderData* data)
{
auto thisPtr{ static_cast<HC_WEBSOCKET*>(data->context) };
switch (op)
{
case XAsyncOp::DoWork: return E_PENDING;
case XAsyncOp::GetResult:
{
auto clientResult{ reinterpret_cast<WebSocketCompletionResult*>(data->buffer) };
*clientResult = thisPtr->m_connectResult;
}
default: return S_OK;
}
}
);
HRESULT hr = connectFunc(uri, subProtocol, this, &m_connectAsyncBlock, info.context, httpSingleton->m_performEnv.get());
if (SUCCEEDED(hr))
{
{
std::lock_guard<std::recursive_mutex> lock{ m_mutex };
m_state = State::Connecting;
}
// Add a ref for the provider. This guarantees the HC_WEBSOCKET is alive until disconnect.
AddRef();
}
return hr;
}
catch (...)
{
HC_TRACE_ERROR(WEBSOCKET, "HCWebSocketConnect [ID %llu]: failed", TO_ULL(id));
return E_FAIL;
}
}
else
{
HC_TRACE_ERROR(WEBSOCKET, "HC_WEBSOCKET::Connect [ID %llu]: Websocket connect implementation not found!", TO_ULL(id));
return E_UNEXPECTED;
}
}