/**************************************************************************** Copyright (c) 2019 Xiamen Yaji Software Co., Ltd. http://www.cocos.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated engine source code (the "Software"), a limited, worldwide, royalty-free, non-assignable, revocable and non-exclusive license to use Cocos Creator solely to develop games on your target platforms. You shall not use Cocos Creator software for developing other software or tools that's used for developing games. You are not granted to publish, distribute, sublicense, and/or sell copies of Cocos Creator. The software or tools in this License Agreement are licensed, not sold. Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #pragma once #include "base/ccConfig.h" #if (USE_SOCKET > 0) && (USE_WEBSOCKET_SERVER > 0) #include "uv.h" #include #include #include #include #include #include #include #include #include #include #include #include "platform/CCPlatformDefine.h" #include "websockets/libwebsockets.h" namespace cocos2d { namespace network { class WebSocketServer; class WebSocketServerConnection; /** * receive/send data buffer with reserved bytes */ class DataFrame { public: DataFrame(const std::string& data); DataFrame(const void* data, int len, bool isBinary = true); virtual ~DataFrame(); void append(unsigned char* p, int len); int slice(unsigned char** p, int len); int consume(int len); int remain() const; inline bool isBinary() const { return _isBinary; } inline bool isString() const { return !_isBinary; } inline bool isFront() const { return _consumed == 0; } void setCallback(std::function callback) { _callback = callback; } void onFinish(const std::string& message) { if (_callback) { _callback(message); } } inline int size() const { return _underlyingData.size() - LWS_PRE; } std::string toString(); unsigned char* getData() { return _underlyingData.data() + LWS_PRE; } private: std::vector _underlyingData; int _consumed = 0; bool _isBinary = false; std::function _callback; }; class CC_DLL WebSocketServerConnection { public: WebSocketServerConnection(struct lws* wsi); virtual ~WebSocketServerConnection(); enum ReadyState { CONNECTING = 1, OPEN = 2, CLOSING = 3, CLOSED = 4 }; void sendTextAsync(const std::string&, std::function callback); void sendBinaryAsync(const void*, size_t len, std::function callback); void closeAsync(int code, std::string reasson); /** stream is not implemented*/ //bool beginBinary(); /** should implement in JS */ // bool send(); // bool sendPing(std::string) //int getSocket(); //std::shared_ptr& getServer(); //std::string& getPath(); inline int getReadyState() const { return (int)_readyState; } std::map getHeaders(); std::vector getProtocols(); inline void setOnClose(std::function cb) { _onclose = cb; } inline void setOnError(std::function cb) { _onerror = cb; } inline void setOnText(std::function)> cb) { _ontext = cb; } inline void setOnBinary(std::function)> cb) { _onbinary = cb; } inline void setOnData(std::function)> cb) { _ondata = cb; } inline void setOnConnect(std::function cb) { _onconnect = cb; } inline void setOnEnd(std::function cb) { _onend = cb; } void onClientCloseInit(); inline void setData(void* d) { _data = d; } inline void* getData() const { return _data; } private: bool send(std::shared_ptr data); bool close(int code, std::string reasson); inline void scheduleSend() { if (_wsi) lws_callback_on_writable(_wsi); } void onConnected(); void onDataReceive(void* in, int len); int onDrainData(); void onHTTP(); void onClientCloseInit(int code, const std::string& msg); void onDestroyClient(); struct lws* _wsi = nullptr; std::map _headers; std::list> _sendQueue; std::shared_ptr _prevPkg; bool _closed = false; std::string _closeReason = "close connection"; int _closeCode = 1000; std::atomic _readyState{ReadyState::CLOSED}; // Attention: do not reference **this** in callbacks std::function _onclose; std::function _onerror; std::function)> _ontext; std::function)> _onbinary; std::function)> _ondata; std::function _onconnect; std::function _onend; uv_async_t _async = {0}; void* _data = nullptr; friend class WebSocketServer; }; class CC_DLL WebSocketServer { public: WebSocketServer(); virtual ~WebSocketServer(); static void listenAsync(std::shared_ptr& server, int port, const std::string& host, std::function callback); void closeAsync(std::function callback = nullptr); std::vector> getConnections() const; void setOnListening(std::function cb) { _onlistening = cb; } void setOnError(std::function cb) { _onerror = cb; } void setOnClose(std::function cb) { _onclose = cb; } void setOnConnection(std::function)> cb) { _onconnection = cb; } inline void setOnEnd(std::function cb) { _onend = cb; } inline void setOnBegin(std::function cb) { _onbegin = cb; } inline void setData(void* d) { _data = d; } inline void* getData() const { return _data; } protected: static void listen(std::shared_ptr server, int port, const std::string& host, std::function callback); bool close(std::function callback = nullptr); void onCreateClient(struct lws* wsi); void onDestroyClient(struct lws* wsi); void onCloseClient(struct lws* wsi); void onCloseClientInit(struct lws* wsi, void* in, int len); void onClientReceive(struct lws* wsi, void* in, int len); int onServerWritable(struct lws* wsi); void onClientHTTP(struct lws* wsi); private: std::shared_ptr findConnection(struct lws *wsi); void destroyContext(); std::string _host; lws_context* _ctx = nullptr; uv_async_t _async = {0}; mutable std::mutex _connsMtx; std::unordered_map > _conns; // Attention: do not reference **this** in callbacks std::function _onlistening; std::function _onerror; std::function _onclose; std::function _onclose_cb; std::function _onend; std::function _onbegin; std::function)> _onconnection; enum class ServerThreadState{ NOT_BOOTED, ST_ERROR, RUNNING, STOPPED, }; std::atomic _serverState {ServerThreadState::NOT_BOOTED}; std::mutex _serverLock; void* _data = nullptr; public: static int _websocketServerCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len); }; } } #endif //#if (USE_SOCKET > 0) && (USE_WEBSOCKET_SERVER > 0)