Some fixes for websocket (#17342)

* libwebsockets & curl are dynamic libraries now, modify project configuration for win32.
Updates WebSocketTest.cpp to test wss connection.

* Corrects some logic in WebSocket.cpp. Adds wss test case. WebSocketTest suggests developers to use `WebSocket::closeAsync` method which will not block current thread.

* mutex unlock fix in WebSocket.cpp. Reverts TEST URL.

* Allows self-assign cert.

* Each ws connection will create a virutal host now. Supports self-signed cert and update test case to suggest developer use ‘closeAsync’.

* Updates deps to 123.
This commit is contained in:
James Chen 2017-02-13 15:15:23 +08:00 committed by minggo
parent f408cbfcca
commit 4237639dd6
10 changed files with 4249 additions and 216 deletions

View File

@ -654,6 +654,8 @@
1AC35DF418CEE65B00F37B72 /* effect1.raw in Resources */ = {isa = PBXBuildFile; fileRef = 1AC35CB518CED84500F37B72 /* effect1.raw */; };
1AC35DF718CEE65B00F37B72 /* effect1.wav in Resources */ = {isa = PBXBuildFile; fileRef = 1AC35CB618CED84500F37B72 /* effect1.wav */; };
1AC35DF818CEE65B00F37B72 /* pew-pew-lei.wav in Resources */ = {isa = PBXBuildFile; fileRef = 1AC35CC418CED84500F37B72 /* pew-pew-lei.wav */; };
1ACF6A521E4C52660033C137 /* cacert.pem in Resources */ = {isa = PBXBuildFile; fileRef = 1ACF6A511E4C52660033C137 /* cacert.pem */; };
1ACF6A531E4C52660033C137 /* cacert.pem in Resources */ = {isa = PBXBuildFile; fileRef = 1ACF6A511E4C52660033C137 /* cacert.pem */; };
1F33634F18E37E840074764D /* RefPtrTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1F33634D18E37E840074764D /* RefPtrTest.cpp */; };
1F33635018E37E840074764D /* RefPtrTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1F33634D18E37E840074764D /* RefPtrTest.cpp */; };
27C5CE011C6E0469000CA4B3 /* SpriteFrameCacheTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C5CDFF1C6E0469000CA4B3 /* SpriteFrameCacheTest.cpp */; };
@ -2363,6 +2365,7 @@
1AC35DAE18CEE5DA00F37B72 /* LuaObjectCBridgeTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LuaObjectCBridgeTest.h; sourceTree = "<group>"; };
1AC35DAF18CEE5DA00F37B72 /* LuaObjectCBridgeTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LuaObjectCBridgeTest.mm; sourceTree = "<group>"; };
1AC35DB018CEE5DA00F37B72 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
1ACF6A511E4C52660033C137 /* cacert.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = cacert.pem; path = "../tests/cpp-tests/Resources/cacert.pem"; sourceTree = "<group>"; };
1D6058910D05DD3D006BFB54 /* cpp-tests Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "cpp-tests Mac.app"; sourceTree = BUILT_PRODUCTS_DIR; };
1F33634D18E37E840074764D /* RefPtrTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RefPtrTest.cpp; sourceTree = "<group>"; };
1F33634E18E37E840074764D /* RefPtrTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefPtrTest.h; sourceTree = "<group>"; };
@ -4417,45 +4420,46 @@
1AC35CA818CED83500F37B72 /* Resources */ = {
isa = PBXGroup;
children = (
460629771CDB4DBA00B44058 /* ccs-res */,
B61E90CA1B12B74B00BE69EA /* NavMesh */,
5046AB5A1AF2C4180060550B /* Materials */,
B603F1B31AC8FBFB00A9579C /* TerrainTest */,
B63993301A49359F00B07923 /* Particle3D */,
15B3709219EE5D1000ABE682 /* Manifests */,
3E2BDB0019C5E5D40055CDCD /* background.wav */,
3E2BDAD119BEA3E20055CDCD /* audio */,
38FA2E75194AECF800FF2BE4 /* ActionTimeline */,
B2507B6A192589AF00FA4972 /* Shaders3D */,
3E92EA841921A7720094CD21 /* Sprite3DTest */,
3EA0FB5D191B92F100B170C8 /* cocosvideo.mp4 */,
1AC35CA918CED84500F37B72 /* animations */,
1AC35CAE18CED84500F37B72 /* ccb */,
1AC35CB318CED84500F37B72 /* components */,
1AC35CB418CED84500F37B72 /* configs */,
1AC35CB818CED84500F37B72 /* extensions */,
1AC35CBA18CED84500F37B72 /* fonts */,
1AC35CBC18CED84500F37B72 /* hd */,
1AC35CBE18CED84500F37B72 /* Images */,
1AC35CC118CED84500F37B72 /* Misc */,
1AC35CC318CED84500F37B72 /* Particles */,
1AC35CC618CED84500F37B72 /* Shaders */,
1AC35CC718CED84500F37B72 /* spine */,
1AC35CC818CED84500F37B72 /* TileMaps */,
1AC35CC918CED84500F37B72 /* zwoptex */,
3E2BDAD119BEA3E20055CDCD /* audio */,
C08689C018D370C90093E810 /* background.caf */,
1AC35CAF18CED84500F37B72 /* CocosBuilderExample.ccbproj */,
1AC35CB018CED84500F37B72 /* CocosBuilderExample.ccbresourcelog */,
1AC35CC218CED84500F37B72 /* music.mid */,
1AC35CAC18CED84500F37B72 /* background.mp3 */,
1AC35CAD18CED84500F37B72 /* background.ogg */,
1AC35CB718CED84500F37B72 /* effect2.ogg */,
1AC35CB918CED84500F37B72 /* fileLookup.plist */,
1AC35CBD18CED84500F37B72 /* Hello.png */,
3E2BDB0019C5E5D40055CDCD /* background.wav */,
1ACF6A511E4C52660033C137 /* cacert.pem */,
1AC35CAE18CED84500F37B72 /* ccb */,
460629771CDB4DBA00B44058 /* ccs-res */,
1AC35CAF18CED84500F37B72 /* CocosBuilderExample.ccbproj */,
1AC35CB018CED84500F37B72 /* CocosBuilderExample.ccbresourcelog */,
3EA0FB5D191B92F100B170C8 /* cocosvideo.mp4 */,
1AC35CB318CED84500F37B72 /* components */,
1AC35CB418CED84500F37B72 /* configs */,
1AC35CB518CED84500F37B72 /* effect1.raw */,
1AC35CB618CED84500F37B72 /* effect1.wav */,
1AC35CB718CED84500F37B72 /* effect2.ogg */,
1AC35CB818CED84500F37B72 /* extensions */,
1AC35CB918CED84500F37B72 /* fileLookup.plist */,
1AC35CBA18CED84500F37B72 /* fonts */,
1AC35CBC18CED84500F37B72 /* hd */,
1AC35CBD18CED84500F37B72 /* Hello.png */,
1AC35CBE18CED84500F37B72 /* Images */,
15B3709219EE5D1000ABE682 /* Manifests */,
5046AB5A1AF2C4180060550B /* Materials */,
1AC35CC118CED84500F37B72 /* Misc */,
1AC35CC218CED84500F37B72 /* music.mid */,
B61E90CA1B12B74B00BE69EA /* NavMesh */,
B63993301A49359F00B07923 /* Particle3D */,
1AC35CC318CED84500F37B72 /* Particles */,
1AC35CC418CED84500F37B72 /* pew-pew-lei.wav */,
1AC35CC618CED84500F37B72 /* Shaders */,
B2507B6A192589AF00FA4972 /* Shaders3D */,
1AC35CC718CED84500F37B72 /* spine */,
3E92EA841921A7720094CD21 /* Sprite3DTest */,
B603F1B31AC8FBFB00A9579C /* TerrainTest */,
29AFEF6619ACCAA000F6B10A /* Test.html */,
1AC35CC818CED84500F37B72 /* TileMaps */,
1AC35CC918CED84500F37B72 /* zwoptex */,
);
name = Resources;
sourceTree = "<group>";
@ -6206,6 +6210,7 @@
1AC35D0618CED84500F37B72 /* spine in Resources */,
3E2F27BB19CFF52A00E7C490 /* background.wav in Resources */,
1AC35CE818CED84500F37B72 /* extensions in Resources */,
1ACF6A521E4C52660033C137 /* cacert.pem in Resources */,
1AC35CDE18CED84500F37B72 /* components in Resources */,
1AC35D0818CED84500F37B72 /* TileMaps in Resources */,
1AC35CE218CED84500F37B72 /* effect1.raw in Resources */,
@ -6517,6 +6522,7 @@
1AC35C8B18CECF1400F37B72 /* Icon-100.png in Resources */,
1AC35D0118CED84500F37B72 /* pew-pew-lei.wav in Resources */,
460629791CDB4DBA00B44058 /* ccs-res in Resources */,
1ACF6A531E4C52660033C137 /* cacert.pem in Resources */,
1AC35D0518CED84500F37B72 /* Shaders in Resources */,
1AC35CD318CED84500F37B72 /* background.ogg in Resources */,
3E2BDB0119C5E5D40055CDCD /* background.wav in Resources */,

View File

@ -76,7 +76,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A\include;$(EngineRoot)external\box2d;$(EngineRoot)external\sqlite3\include;$(EngineRoot)external\unzip;$(EngineRoot)external\edtaa3func;$(EngineRoot)external\tinyxml2;$(EngineRoot)external\png\include\win32;$(EngineRoot)external\jpeg\include\win32;$(EngineRoot)external\tiff\include\win32;$(EngineRoot)external\webp\include\win32;$(EngineRoot)external\freetype2\include\win32;$(EngineRoot)external\win32-specific\OpenalSoft\include;$(EngineRoot)external\win32-specific\MP3Decoder\include;$(EngineRoot)external\win32-specific\OggDecoder\include;$(EngineRoot)external\win32-specific\icon\include;$(EngineRoot)external\win32-specific\zlib\include;$(EngineRoot)external\chipmunk\include;$(EngineRoot)external\xxhash;$(EngineRoot)external\ConvertUTF;$(EngineRoot)external\curl\include\win32;$(EngineRoot)external\websockets\include\win32;$(EngineRoot)external\poly2tri\common;$(EngineRoot)external\poly2tri\sweep;$(EngineRoot)external\poly2tri;$(EngineRoot)external;$(EngineRoot)cocos;$(EngineRoot)cocos\editor-support;$(EngineRoot)cocos\platform\win8.1-universal;$(EngineRoot)extensions;$(EngineRoot);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_USRDLL;_DEBUG;_WINDOWS;_LIB;CURL_STATICLIB;COCOS2DXWIN32_EXPORTS;GL_GLEXT_PROTOTYPES;COCOS2D_DEBUG=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE3DDLL;_EXPORT_DLL_;_USRSTUDIODLL;_USREXDLL;_USEGUIDLL;CC_ENABLE_CHIPMUNK_INTEGRATION=1;PROTOBUF_USE_DLLS;LIBPROTOBUF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_USRDLL;_DEBUG;_WINDOWS;_LIB;LWS_DLL;COCOS2DXWIN32_EXPORTS;GL_GLEXT_PROTOTYPES;COCOS2D_DEBUG=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE3DDLL;_EXPORT_DLL_;_USRSTUDIODLL;_USREXDLL;_USEGUIDLL;CC_ENABLE_CHIPMUNK_INTEGRATION=1;PROTOBUF_USE_DLLS;LIBPROTOBUF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -118,7 +118,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\openssl\prebuilt\win32\*.*" "$(OutDir)"
<ImportLibrary>$(TargetDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<ModuleDefinitionFile>cocos2d.def</ModuleDefinitionFile>
<AdditionalDependencies>sqlite3.lib;libcurl_a.lib;websockets_static.lib;libcrypto.lib;libssl.lib;libmpg123.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;libbullet.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>sqlite3.lib;libcurl.lib;websockets.lib;libcrypto.lib;libssl.lib;libmpg123.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;libbullet.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
@ -132,7 +132,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\openssl\prebuilt\win32\*.*" "$(OutDir)"
</PreBuildEvent>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A\include;$(EngineRoot)external\sqlite3\include;$(EngineRoot)external\unzip;$(EngineRoot)external\edtaa3func;$(EngineRoot)external\tinyxml2;$(EngineRoot)external\png\include\win32;$(EngineRoot)external\jpeg\include\win32;$(EngineRoot)external\tiff\include\win32;$(EngineRoot)external\webp\include\win32;$(EngineRoot)external\freetype2\include\win32;$(EngineRoot)external\win32-specific\MP3Decoder\include;$(EngineRoot)external\win32-specific\OggDecoder\include;$(EngineRoot)external\win32-specific\OpenalSoft\include;$(EngineRoot)external\win32-specific\icon\include;$(EngineRoot)external\win32-specific\zlib\include;$(EngineRoot)external\chipmunk\include;$(EngineRoot)external\xxhash;$(EngineRoot)external\ConvertUTF;$(EngineRoot)external\Box2d;$(EngineRoot)external\curl\include\win32;$(EngineRoot)external\websockets\include\win32\;$(EngineRoot)external\poly2tri\common;$(EngineRoot)external\poly2tri\sweep;$(EngineRoot)external\poly2tri;$(EngineRoot)external;$(EngineRoot)cocos;$(EngineRoot)cocos\editor-support;$(EngineRoot)cocos\platform\win8.1-universal;$(EngineRoot)extensions;$(EngineRoot);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_USRDLL;NDEBUG;_WINDOWS;_LIB;CURL_STATICLIB;COCOS2DXWIN32_EXPORTS;GL_GLEXT_PROTOTYPES;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE3DDLL;_EXPORT_DLL_;_USRSTUDIODLL;_USREXDLL;_USEGUIDLL;CC_ENABLE_CHIPMUNK_INTEGRATION=1;PROTOBUF_USE_DLLS;LIBPROTOBUF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_USRDLL;NDEBUG;_WINDOWS;_LIB;LWS_DLL;COCOS2DXWIN32_EXPORTS;GL_GLEXT_PROTOTYPES;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE3DDLL;_EXPORT_DLL_;_USRSTUDIODLL;_USREXDLL;_USEGUIDLL;CC_ENABLE_CHIPMUNK_INTEGRATION=1;PROTOBUF_USE_DLLS;LIBPROTOBUF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
@ -165,7 +165,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\chipmunk\prebuilt\win32\release-lib\*.*
xcopy /Y /Q "$(ProjectDir)..\..\external\openssl\prebuilt\win32\*.*" "$(OutDir)"</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>sqlite3.lib;libcurl_a.lib;websockets_static.lib;libcrypto.lib;libssl.lib;libmpg123.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;libbullet.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>sqlite3.lib;libcurl.lib;websockets.lib;libcrypto.lib;libssl.lib;libmpg123.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;libbullet.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(ProjectName).dll</OutputFile>
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>LIBCMTD.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>

View File

@ -189,56 +189,9 @@ static std::string getFileNameForPath(const std::string& filePath)
}
#endif
static size_t getProtocolCount(struct lws_protocols* protocols)
{
struct lws_protocols *p = protocols;
for (;p != nullptr && p->callback != nullptr; ++p);
return p - protocols;
}
static struct lws_protocols* deepCopyProtocols(struct lws_protocols* protocols)
{
if (protocols == nullptr)
return nullptr;
size_t protocolCount = getProtocolCount(protocols);
struct lws_protocols* copied = (struct lws_protocols*)malloc((protocolCount + 1) * sizeof(struct lws_protocols));
memcpy(copied, protocols, (protocolCount + 1) * sizeof(struct lws_protocols));
const char* srcName = nullptr;
char* dstName = nullptr;
for (size_t i = 0; i < protocolCount; ++i)
{
srcName = protocols[i].name;
dstName = nullptr;
if (srcName != nullptr)
{
size_t srcLen = strlen(srcName);
dstName = (char*)malloc(srcLen+1);
dstName[srcLen] = '\0';
if (srcLen > 0)
{
strcpy(dstName, srcName);
}
}
copied[i].name = dstName;
}
return copied;
}
struct WsCache
{
struct lws_protocols* keyProtocols; // end with nullptr
struct lws_vhost* valueVHost;
std::string ca;
};
static struct lws_protocols __defaultProtocols[2];
static std::vector<struct WsCache*> __wsProtocolVHostCache;
static lws_context_creation_info convertToContextCreationInfo(const struct lws_protocols* protocols)
static lws_context_creation_info convertToContextCreationInfo(const struct lws_protocols* protocols, bool peerServerCert)
{
lws_context_creation_info info;
memset(&info, 0, sizeof(info));
@ -264,7 +217,14 @@ static lws_context_creation_info convertToContextCreationInfo(const struct lws_p
info.gid = -1;
info.uid = -1;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
if (peerServerCert)
{
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
}
else
{
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED;
}
info.user = nullptr;
return info;
@ -408,7 +368,14 @@ void WsThreadHelper::onSubThreadLoop()
}
__wsHelper->_subThreadWsMessageQueueMutex.unlock();
lws_service(__wsContext, 50);
// The second parameter passed to 'lws_service' means the timeout in milliseconds while polling websocket events.
// The lower value the better, otherwise, it may trigger high CPU usage.
// We set 2ms in 'lws_service' then sleep 3ms to make lower CPU cost.
// Since messages are received in websocket thread and user code is in cocos thread, we need to post event to
// cocos thread and trigger user callbacks by 'Scheduler::performFunctionInCocosThread'. If game's fps is set
// to 60 (16.66ms), the latency will be (2ms + 3ms + 16.66ms + internet delay) > 21ms
lws_service(__wsContext, 2);
std::this_thread::sleep_for(std::chrono::milliseconds(3));
}
}
@ -424,7 +391,7 @@ void WsThreadHelper::onSubThreadStarted()
__defaultProtocols[0].rx_buffer_size = WS_RX_BUFFER_SIZE;
__defaultProtocols[0].id = std::numeric_limits<uint32_t>::max();
lws_context_creation_info creationInfo = convertToContextCreationInfo(__defaultProtocols);
lws_context_creation_info creationInfo = convertToContextCreationInfo(__defaultProtocols, true);
__wsContext = lws_create_context(&creationInfo);
}
@ -594,18 +561,6 @@ WebSocket::~WebSocket()
LOGD("after join ws thread\n");
CC_SAFE_DELETE(__wsHelper);
for (auto& cache : __wsProtocolVHostCache)
{
struct lws_protocols *p = cache->keyProtocols;
for (;p != nullptr && p->callback != nullptr; ++p)
{
free((void*)p->name);
}
free(cache->keyProtocols);
delete cache;
}
__wsProtocolVHostCache.clear();
}
Director::getInstance()->getEventDispatcher()->removeEventListener(_resetDirectorListener);
@ -764,7 +719,7 @@ void WebSocket::close()
return;
}
_readyState = State::CLOSED;
_readyState = State::CLOSING;
_readyStateMutex.unlock();
}
@ -791,13 +746,13 @@ void WebSocket::closeAsync()
LOGD("closeAsync: WebSocket (%p) is closing...\n", this);
std::lock_guard<std::mutex> lk(_readyStateMutex);
if (_readyState == State::CLOSED)
if (_readyState == State::CLOSED || _readyState == State::CLOSING)
{
LOGD("closeAsync: WebSocket (%p) was closed, no need to close it again!\n", this);
return;
}
_readyState = State::CLOSED;
_readyState = State::CLOSING;
}
WebSocket::State WebSocket::getReadyState()
@ -806,43 +761,21 @@ WebSocket::State WebSocket::getReadyState()
return _readyState;
}
struct WsCache* WebSocket::getOrCreateVhost(struct lws_protocols* protocols)
struct lws_vhost* WebSocket::createVhost(struct lws_protocols* protocols)
{
struct WsCache* ret = nullptr;
for (auto& cache : __wsProtocolVHostCache)
auto fileUtils = FileUtils::getInstance();
bool isCAFileExist = fileUtils->isFileExist(_caFilePath);
if (isCAFileExist)
{
size_t protocolCountInCache = getProtocolCount(cache->keyProtocols);
size_t protocolCount = getProtocolCount(protocols);
if (protocolCountInCache == protocolCount)
{
for (size_t i = 0; i < protocolCount; ++i)
{
if (cache->keyProtocols[i].name != nullptr && protocols[i].name != nullptr
&& 0 == strcmp(cache->keyProtocols[i].name, protocols[i].name))
{
ret = cache;
break;
}
}
if (ret != nullptr)
{
break;
}
}
_caFilePath = fileUtils->fullPathForFilename(_caFilePath);
}
if (ret == nullptr)
{
struct lws_protocols* copied = deepCopyProtocols(protocols);
lws_context_creation_info info = convertToContextCreationInfo(copied);
ret = new (std::nothrow) struct WsCache();
lws_context_creation_info info = convertToContextCreationInfo(protocols, isCAFileExist);
if (_SSLConnection != 0)
if (_SSLConnection != 0)
{
if (isCAFileExist)
{
CCASSERT(!_caFilePath.empty(), "CA file path is empty!");
auto fileUtils = FileUtils::getInstance();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
// if ca file is in the apk, try to extract it to writable path
std::string writablePath = fileUtils->getWritablePath();
@ -899,38 +832,19 @@ struct WsCache* WebSocket::getOrCreateVhost(struct lws_protocols* protocols)
}
}
#else
if (fileUtils->isFileExist(_caFilePath))
{
_caFilePath = fileUtils->fullPathForFilename(_caFilePath);
info.ssl_ca_filepath = _caFilePath.c_str();
}
else
{
CCASSERT(false, "CA file doesn't exist!");
}
info.ssl_ca_filepath = _caFilePath.c_str();
#endif
}
lws_vhost* vhost = lws_create_vhost(__wsContext, &info);
if (vhost != nullptr)
{
ret->keyProtocols = copied;
ret->valueVHost = vhost;
__wsProtocolVHostCache.push_back(ret);
}
else
{
struct lws_protocols *p = copied;
for (;p != nullptr && p->callback != nullptr; ++p)
{
free((void*)p->name);
}
free(copied);
CC_SAFE_DELETE(ret);
LOGD("WARNING: CA Root file isn't set. SSL connection will not peer server certificate\n");
_SSLConnection = _SSLConnection | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
}
}
return ret;
lws_vhost* vhost = lws_create_vhost(__wsContext, &info);
return vhost;
}
void WebSocket::onClientOpenConnectionRequest()
@ -958,15 +872,14 @@ void WebSocket::onClientOpenConnectionRequest()
_readyState = State::CONNECTING;
_readyStateMutex.unlock();
struct WsCache* cache = nullptr;
struct lws_vhost* vhost = nullptr;
if (_protocols != nullptr)
{
cache = getOrCreateVhost(_protocols);
vhost = createVhost(_protocols);
}
else
{
cache = getOrCreateVhost(__defaultProtocols);
vhost = createVhost(__defaultProtocols);
}
struct lws_client_connect_info connectInfo;
@ -982,7 +895,7 @@ void WebSocket::onClientOpenConnectionRequest()
connectInfo.ietf_version_or_minus_one = -1;
connectInfo.userdata = this;
connectInfo.client_exts = exts;
connectInfo.vhost = cache->valueVHost;
connectInfo.vhost = vhost;
_wsInstance = lws_client_connect_via_info(&connectInfo);
@ -994,7 +907,7 @@ void WebSocket::onClientOpenConnectionRequest()
}
else
{
CCLOGERROR("Create websocket context failed!");
LOGE("Create websocket context failed!");
}
}
@ -1003,7 +916,7 @@ int WebSocket::onClientWritable()
// LOGD("onClientWritable ... \n");
{
std::lock_guard<std::mutex> readMutex(_readyStateMutex);
if (_readyState == State::CLOSED)
if (_readyState == State::CLOSING)
{
LOGD("Closing websocket (%p) connection.\n", this);
return -1;
@ -1263,11 +1176,10 @@ int WebSocket::onConnectionOpened()
int WebSocket::onConnectionError()
{
LOGD("WebSocket (%p) onConnectionError ...\n", this);
{
std::lock_guard<std::mutex> lk(_readyStateMutex);
if (_readyState == State::CLOSED || _readyState == State::CLOSING)
LOGD("WebSocket (%p) onConnectionError, state: %d ...\n", this, (int)_readyState);
if (_readyState == State::CLOSED)
{
return 0;
}
@ -1286,34 +1198,55 @@ int WebSocket::onConnectionError()
}
});
onConnectionClosed();
return 0;
}
int WebSocket::onConnectionClosed()
{
LOGD("WebSocket (%p) onConnectionClosed ...\n", this);
_readyStateMutex.lock();
if (_readyState == State::CLOSED)
{
LOGD("onConnectionClosed: WebSocket (%p) was closed, no need to close it again!\n", this);
_readyStateMutex.unlock();
for(;;)
std::lock_guard<std::mutex> lk(_readyStateMutex);
LOGD("WebSocket (%p) onConnectionClosed, state: %d ...\n", this, (int)_readyState);
if (_readyState == State::CLOSED)
{
std::lock_guard<std::mutex> lk(_closeMutex);
_closeCondition.notify_one();
if (_closeState != CloseState::SYNC_CLOSING)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
return 0;
}
return 0;
}
_readyState = State::CLOSED;
_readyStateMutex.unlock();
if (_readyState == State::CLOSING)
{
if (_closeState == CloseState::SYNC_CLOSING)
{
LOGD("onConnectionClosed, WebSocket (%p) is closing by client synchronously.\n", this);
for(;;)
{
std::lock_guard<std::mutex> lkClose(_closeMutex);
_closeCondition.notify_one();
if (_closeState == CloseState::SYNC_CLOSED)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 0;
}
else if (_closeState == CloseState::ASYNC_CLOSING)
{
LOGD("onConnectionClosed, WebSocket (%p) is closing by client asynchronously.\n", this);
}
else
{
LOGD("onConnectionClosed, WebSocket (%p) is closing by server.\n", this);
}
}
else
{
LOGD("onConnectionClosed, WebSocket (%p) is closing by server.\n", this);
}
_readyState = State::CLOSED;
}
std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
__wsHelper->sendMessageToCocosThread([this, isDestroyed](){

View File

@ -42,7 +42,7 @@
struct lws;
struct lws_protocols;
struct lws_vhost;
/**
* @addtogroup network
* @{
@ -55,7 +55,6 @@ class EventListenerCustom;
namespace network {
class WsThreadHelper;
struct WsCache;
/**
* WebSocket is wrapper of the libwebsockets-protocol, let the develop could call the websocket easily.
@ -225,7 +224,7 @@ private:
int onConnectionError();
int onConnectionClosed();
struct WsCache* getOrCreateVhost(struct lws_protocols* protocols);
struct lws_vhost* createVhost(struct lws_protocols* protocols);
private:

View File

@ -290,22 +290,22 @@ bool js_cocos2dx_extension_WebSocket_constructor(JSContext *cx, uint32_t argc, j
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (argc == 1 || argc == 2)
if (argc == 1 || argc == 2 || argc == 3)
{
std::string url;
do {
bool ok = jsval_to_std_string(cx, args.get(0), &url);
JSB_PRECONDITION2( ok, cx, false, "Error processing arguments");
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
} while (0);
JS::RootedObject proto(cx, js_cocos2dx_websocket_prototype);
JS::RootedObject obj(cx, JS_NewObject(cx, js_cocos2dx_websocket_class, proto, JS::NullPtr()));
//JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, js_cocos2dx_websocket_class, args));
WebSocket* cobj = nullptr;
if (argc == 2)
if (argc >= 2)
{
std::string caFilePath;
std::vector<std::string> protocols;
if (args.get(1).isString())
@ -313,7 +313,7 @@ bool js_cocos2dx_extension_WebSocket_constructor(JSContext *cx, uint32_t argc, j
std::string protocol;
do {
bool ok = jsval_to_std_string(cx, args.get(1), &protocol);
JSB_PRECONDITION2( ok, cx, false, "Error processing arguments");
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
} while (0);
protocols.push_back(protocol);
}
@ -321,29 +321,37 @@ bool js_cocos2dx_extension_WebSocket_constructor(JSContext *cx, uint32_t argc, j
{
bool ok = true;
JS::RootedObject arg2(cx, args.get(1).toObjectOrNull());
JSB_PRECONDITION(JS_IsArrayObject( cx, arg2 ), "Object must be an array");
JSB_PRECONDITION(JS_IsArrayObject(cx, arg2), "Object must be an array");
uint32_t len = 0;
JS_GetArrayLength(cx, arg2, &len);
for( uint32_t i=0; i< len;i++ )
for (uint32_t i = 0; i< len; i++)
{
JS::RootedValue valarg(cx);
JS_GetElement(cx, arg2, i, &valarg);
std::string protocol;
do {
ok = jsval_to_std_string(cx, valarg, &protocol);
JSB_PRECONDITION2( ok, cx, false, "Error processing arguments");
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
} while (0);
protocols.push_back(protocol);
}
}
if (argc > 2)
{
do {
bool ok = jsval_to_std_string(cx, args.get(2), &caFilePath);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
} while (0);
}
cobj = new (std::nothrow) WebSocket();
JSB_WebSocketDelegate* delegate = new (std::nothrow) JSB_WebSocketDelegate();
delegate->setJSDelegate(obj);
cobj->init(*delegate, url, &protocols);
cobj->init(*delegate, url, &protocols, caFilePath);
}
else
{

View File

@ -1,5 +1,5 @@
{
"version": "v3-deps-121",
"version": "v3-deps-123",
"zip_file_size": "112893722",
"repo_name": "cocos2d-x-3rd-party-libs-bin",
"repo_parent": "https://github.com/cocos2d/",

View File

@ -75,15 +75,27 @@ WebSocketTest::WebSocketTest()
}
WebSocketTest::~WebSocketTest()
{
}
void WebSocketTest::onExit()
{
if (_wsiSendText)
_wsiSendText->close();
{
_wsiSendText->closeAsync();
}
if (_wsiSendBinary)
_wsiSendBinary->close();
{
_wsiSendBinary->closeAsync();
}
if (_wsiError)
_wsiError->close();
{
_wsiError->closeAsync();
}
Node::onExit();
}
void WebSocketTest::startTestCallback(Ref* sender)
@ -95,20 +107,36 @@ void WebSocketTest::startTestCallback(Ref* sender)
_wsiSendBinary = new network::WebSocket();
_wsiError = new network::WebSocket();
if (!_wsiSendText->init(*this, "ws://echo.websocket.org"))
std::vector<std::string> protocols;
protocols.push_back("myprotocol_1");
protocols.push_back("myprotocol_2");
if (!_wsiSendText->init(*this, "wss://echo.websocket.org", &protocols, "cacert.pem"))
{
CC_SAFE_DELETE(_wsiSendText);
}
else
{
retain(); // Retain self to avoid WebSocketTest instance be deleted immediately, it will be released in WebSocketTest::onClose.
}
if (!_wsiSendBinary->init(*this, "ws://echo.websocket.org"))
protocols.pop_back();
if (!_wsiSendBinary->init(*this, "wss://echo.websocket.org", &protocols))
{
CC_SAFE_DELETE(_wsiSendBinary);
}
else
{
retain(); // Retain self to avoid WebSocketTest instance be deleted immediately, it will be released in WebSocketTest::onClose.
}
if (!_wsiError->init(*this, "ws://invalid.url.com"))
if (!_wsiError->init(*this, "ws://invalid.urlxxxxxxxx.com"))
{
CC_SAFE_DELETE(_wsiError);
}
else
{
retain(); // Retain self to avoid WebSocketTest instance be deleted immediately, it will be released in WebSocketTest::onClose.
}
}
// Delegate methods
@ -168,30 +196,44 @@ void WebSocketTest::onMessage(network::WebSocket* ws, const network::WebSocket::
void WebSocketTest::onClose(network::WebSocket* ws)
{
log("websocket instance (%p) closed.", ws);
log("onClose: websocket instance (%p) closed.", ws);
if (ws == _wsiSendText)
{
_wsiSendText = nullptr;
_sendTextStatus->setString("Send Text WS was closed");
}
else if (ws == _wsiSendBinary)
{
_wsiSendBinary = nullptr;
_sendBinaryStatus->setString("Send Binary WS was closed");
}
else if (ws == _wsiError)
{
_wsiError = nullptr;
_errorStatus->setString("Test invalid URL WS was closed");
}
// Delete websocket instance.
CC_SAFE_DELETE(ws);
log("WebSocketTest ref: %u", _referenceCount);
release();
}
void WebSocketTest::onError(network::WebSocket* ws, const network::WebSocket::ErrorCode& error)
{
log("Error was fired, error code: %d", static_cast<int>(error));
if (ws == _wsiError)
char buf[100] = {0};
sprintf(buf, "An error was fired, code: %d", static_cast<int>(error));
if (ws == _wsiSendText)
{
_sendTextStatus->setString(buf);
}
else if (ws == _wsiSendBinary)
{
_sendBinaryStatus->setString(buf);
}
else if (ws == _wsiError)
{
char buf[100] = {0};
sprintf(buf, "an error was fired, code: %d", static_cast<int>(error));
_errorStatus->setString(buf);
}
}

View File

@ -24,6 +24,8 @@ public:
WebSocketTest();
virtual ~WebSocketTest();
virtual void onExit() override;
virtual void onOpen(cocos2d::network::WebSocket* ws)override;
virtual void onMessage(cocos2d::network::WebSocket* ws, const cocos2d::network::WebSocket::Data& data)override;

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\Classes;$(EngineRoot);$(EngineRoot)cocos;$(EngineRoot)cocos\editor-support;$(EngineRoot)external;$(EngineRoot)external\chipmunk\include;$(EngineRoot)external\curl\include\win32;$(EngineRoot)external\websockets\win32\include;$(EngineRoot)extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USE_MATH_DEFINES;CURL_STATICLIB;GL_GLEXT_PROTOTYPES;CC_ENABLE_CHIPMUNK_INTEGRATION=1;COCOS2D_DEBUG=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;COCOS2DXWIN32_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USE_MATH_DEFINES;GL_GLEXT_PROTOTYPES;CC_ENABLE_CHIPMUNK_INTEGRATION=1;COCOS2D_DEBUG=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;COCOS2DXWIN32_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
@ -88,7 +88,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalDependencies>libcurl_a.lib;libssl.lib;libcrypto.lib;opengl32.lib;glew32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>libcurl.lib;libssl.lib;libcrypto.lib;opengl32.lib;glew32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
@ -111,7 +111,7 @@ xcopy "$(OutDir)..\*.dll" "$(OutDir)" /D /Y</Command>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\Classes;$(EngineRoot);$(EngineRoot)cocos;$(EngineRoot)cocos\editor-support;$(EngineRoot)external;$(EngineRoot)external\chipmunk\include;$(EngineRoot)external\curl\include\win32;$(EngineRoot)external\websockets\win32\include;$(EngineRoot)extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;CURL_STATICLIB;_USE_MATH_DEFINES;GL_GLEXT_PROTOTYPES;CC_ENABLE_CHIPMUNK_INTEGRATION=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USE_MATH_DEFINES;GL_GLEXT_PROTOTYPES;CC_ENABLE_CHIPMUNK_INTEGRATION=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<WarningLevel>Level3</WarningLevel>
@ -121,7 +121,7 @@ xcopy "$(OutDir)..\*.dll" "$(OutDir)" /D /Y</Command>
<MinimalRebuild>false</MinimalRebuild>
</ClCompile>
<Link>
<AdditionalDependencies>libcurl_a.lib;libssl.lib;libcrypto.lib;opengl32.lib;glew32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>libcurl.lib;libssl.lib;libcrypto.lib;opengl32.lib;glew32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(ProjectName).exe</OutputFile>
<AdditionalLibraryDirectories>$(OutDir);$(SolutionDir)$(Configuration).win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>false</GenerateDebugInformation>