I have this function which on Windows has performed without problems for years already:
Code: Select all
// This function connects to the server
bool CWebSocketClient::Connect(std::wstring *pWstrDebugLog, const wchar_t *pwcServerIpAddress, uint16 u16ServerIpPort /* INTERNET_DEFAULT_PORT */, bool bEncryptedConnection /* true */, HANDLE hEvtNotifyNewMsg /* nullptr */)
{
m_pWstrDebugLog = pWstrDebugLog;
ENTER_FUNCTION_DEBUG_LOG_TO_MEM
// Aren't websockets available?
#ifdef BUILD_WEB_SOCKETS_UNAVAILABLE
// Do nothing
UNREFERENCED_PARAMETER(pwcServerIpAddress);
UNREFERENCED_PARAMETER(u16ServerIpPort);
UNREFERENCED_PARAMETER(bEncryptedConnection);
UNREFERENCED_PARAMETER(hEvtNotifyNewMsg);
#else
try
{
CHECK_MEM_LEAKS
int32 i32Pos;
// Do we already have a session?
if(m_hWebSocketSession)
Disconnect();
// We are going to use asynchronous mode
m_bSynchronousMode = false;
// Store the settings
m_u16ServerIpPort = u16ServerIpPort;
m_hEvtNotifyParentNewMsg = hEvtNotifyNewMsg;
// Get the address. Skip the protocol (e.g., wss://, https://, ...)
if((i32Pos = FindInString(pwcServerIpAddress, L"://")) >= 0)
i32Pos += 3;
else
i32Pos = 0;
m_wstrServerIpAddress = &pwcServerIpAddress[i32Pos];
// Split the address in a base URL and the path
if((m_wstrServerIpAddress.GetLength() < gc_i32Max) &&
((i32Pos = static_cast<int32>(m_wstrServerIpAddress.Find(L"/"))) > 0))
{
// Isn't this the last character?
if(m_wstrServerIpAddress.GetLength() > static_cast<uint64>(i32Pos+1))
m_wstrServerPath = &m_wstrServerIpAddress[i32Pos+1];
m_wstrServerIpAddress.Erase(static_cast<uint32>(i32Pos), static_cast<uint32>(m_wstrServerIpAddress.GetLength()-i32Pos));
}
else
m_wstrServerPath.Clear();
// Create a session
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() : %s:%u\n", m_wstrServerIpAddress.GetString(), u16ServerIpPort);
if((m_hWebSocketSession = WinHttpOpen(L"Dms", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, nullptr, nullptr, WINHTTP_FLAG_ASYNC | (bEncryptedConnection ? WINHTTP_FLAG_SECURE_DEFAULTS : 0))) != nullptr)
{
// Connect to the server
if((m_hWebSocketConnection = WinHttpConnect(m_hWebSocketSession, m_wstrServerIpAddress.GetString(), m_u16ServerIpPort > 0 ? m_u16ServerIpPort : INTERNET_DEFAULT_PORT, 0)) != nullptr)
{
// Get a request handle
if((m_hWebSocketRequest = WinHttpOpenRequest(m_hWebSocketConnection, L"GET", m_wstrServerPath.GetString(), nullptr, nullptr, nullptr, bEncryptedConnection ? WINHTTP_FLAG_SECURE : 0)) != nullptr)
{
// Request a protocol upgrade from http to web socket
if(WinHttpSetOption(m_hWebSocketRequest, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, nullptr, 0))
{
bool bError{false};
// Initiate a web socket handshake by sending a request
while(!bError)
{
// Start the handshake
if(WinHttpSendRequest(m_hWebSocketRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
// We successfully started the handshake
break;
}
else
{
const DWORD dwResult = GetLastError();
// What error occurred?
if(dwResult == ERROR_WINHTTP_SECURE_FAILURE)
{
DWORD dwFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
// We can try ignoring certificate errors, for instance because of self-signed certificates
if(WinHttpSetOption(m_hWebSocketRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags)))
{
// We should retry
OutputDebugMessageNoFormat(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpSendRequest() : dwError = ERROR_WINHTTP_SECURE_FAILURE -> trying non-secure fallback\n");
}
else
{
GetSystemErrorDesc(m_pWstrDebugLog, dwResult, L"CWebSocketClient::Connect() -> WinHttpSetOption(m_hWebSocketRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags))");
bError = true;
}
}
else if(dwResult == ERROR_WINHTTP_RESEND_REQUEST)
{
// We should retry
OutputDebugMessageNoFormat(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpSendRequest() : dwError = ERROR_WINHTTP_RESEND_REQUEST -> retrying handshake\n");
}
else
{
// An unknown error occurred
GetSystemErrorDesc(m_pWstrDebugLog, dwResult, L"CWebSocketClient::Connect() -> WinHttpSendRequest(m_hWebSocketRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)");
bError = true;
}
}
}
// Did the handshake negotiation succeed?
if(!bError)
{
// Get the response
if(WinHttpReceiveResponse(m_hWebSocketRequest, nullptr))
{
uint32 u32StatusCode{0};
DWORD dwSize{sizeof(u32StatusCode)};
// Get the headers
if(WinHttpQueryHeaders(m_hWebSocketRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &u32StatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX))
{
// Can we switch the protocol to web socket?
if(u32StatusCode == HTTP_STATUS_SWITCH_PROTOCOLS)
{
// Complete the handshake
if((m_hWebSocket = WinHttpWebSocketCompleteUpgrade(m_hWebSocketRequest, NULL)) != nullptr)
{
DWORD_PTR dwOption{DWORD_PTR(this)};
OutputDebugMessage(m_pWstrDebugLog, L"this = 0x%p m_hWebSocket = 0x%p\n", this, m_hWebSocket);
// Free the request handle. From now on we can use the web socket handle
SAFE_CLOSE_WIN_HTTP_HANDLE(m_hWebSocketRequest);
// Set the context handle
if(WinHttpSetOption(m_hWebSocket, WINHTTP_OPTION_CONTEXT_VALUE, &dwOption, sizeof(dwOption)))
{
// Get notification messages
if(WinHttpSetStatusCallback(m_hWebSocket, OnEventCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL) != WINHTTP_INVALID_STATUS_CALLBACK)
{
DWORD dwBytesRecv{0}, dwError;
WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType{WINHTTP_WEB_SOCKET_BUFFER_TYPE::WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE};
// We are connected now
ResetEvent(m_hEvtWebSocketClosed);
m_bConnected = true;
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> successfully connected web socket \"%s::%u\"\n", m_wstrServerIpAddress.GetString(), m_u16ServerIpPort > 0 ? m_u16ServerIpPort : INTERNET_DEFAULT_PORT);
// Allow the other side to start sending us data
if((dwError = WinHttpWebSocketReceive(m_hWebSocket, &m_pu8ReadBuffer[m_u32ReadBufferLen], m_u32MaxReadBufferLen - m_u32ReadBufferLen, &dwBytesRecv, &eBufferType)) != NO_ERROR)
{
// Which error occurred?
if(dwError == ERROR_INVALID_OPERATION)
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpWebSocketReceive() : dwError = %i (ERROR_INVALID_OPERATION : A close or receive is pending, or the receive channel has already been closed)\n", dwError);
else if(dwError == ERROR_INVALID_PARAMETER)
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpWebSocketReceive() : dwError = %i (ERROR_INVALID_PARAMETER)\n", dwError);
else if(dwError == ERROR_WINHTTP_INVALID_SERVER_RESPONSE)
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpWebSocketReceive() : dwError = %i (ERROR_WINHTTP_INVALID_SERVER_RESPONSE)\n", dwError);
else if(dwError == ERROR_WINHTTP_OPERATION_CANCELLED)
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpWebSocketReceive() : dwError = %i (ERROR_WINHTTP_OPERATION_CANCELLED : The operation was cancelled because WinHttpWebSocketClose was called to close the connection)\n", dwError);
else
OutputDebugMessage(m_pWstrDebugLog, L"CWebSocketClient::Connect() -> WinHttpWebSocketReceive() : dwError = %i (unknown error)\n", dwError);
}
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpSetOption()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpWebSocketCompleteUpgrade()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpWebSocketCompleteUpgrade()");
}
else
OutputDebugMessage(m_pWstrDebugLog, CWString().Format(L"CWebSocketClient::Connect() -> WinHttpQueryHeaders() : expected u32StatusCode = %u received u32StatusCode = %u\n", HTTP_STATUS_SWITCH_PROTOCOLS, u32StatusCode).GetString());
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), CWString().Format(L"CWebSocketClient::Connect() -> WinHttpQueryHeaders() : u32StatusCode = %u dwSize = %u", u32StatusCode, dwSize));
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpReceiveResponse()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpSendRequest()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpSetOption()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpOpenRequest()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpConnect()");
}
else
GetSystemErrorDesc(m_pWstrDebugLog, GetLastError(), L"CWebSocketClient::Connect() -> WinHttpOpen()");
// Didn't we successfully connect?
if(!m_bConnected)
Disconnect();
}
catch(...) { OnException(m_pWstrDebugLog, __FILE__, __LINE__, __FUNCSIG__, __TIMESTAMP__, nullptr); }
#endif // #ifdef BUILD_WEB_SOCKETS_UNAVAILABLE
return IsConnected();
}
Code: Select all
if(WinHttpQueryHeaders(m_hWebSocketRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &u32StatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX))
Is there something different I need to do in Wine compared to Windows for web sockets? Help would be greatly appreciated, because this problem is stopping us from switching from Windows to Linux.