From b5cf33a582b8cad3b9f6eba91cd510abd22421e0 Mon Sep 17 00:00:00 2001 From: Andreas Hausladen Date: Mon, 10 Jan 2022 19:34:24 +0100 Subject: [PATCH] Introduction of IXWebSocketSendData (#347) * Introduction of IXWebSocketSendData that makes it possible to not only send std::string but also std::vector and char* without copying them to a std::string first. Add a sendUtf8Text() method that doesn't check for invalid UTF-8 characters. The caller must guarantee that the string only contains valid UTF-8 characters. * Updated usage.md: sendUtf8Text() and IXWebSocketSendData --- CMakeLists.txt | 1 + docs/usage.md | 12 ++ ixwebsocket/IXWebSocket.cpp | 30 +++- ixwebsocket/IXWebSocket.h | 13 +- ixwebsocket/IXWebSocketPerMessageDeflate.cpp | 5 + ixwebsocket/IXWebSocketPerMessageDeflate.h | 2 + .../IXWebSocketPerMessageDeflateCodec.cpp | 6 + .../IXWebSocketPerMessageDeflateCodec.h | 2 + ixwebsocket/IXWebSocketSendData.h | 128 ++++++++++++++++++ ixwebsocket/IXWebSocketTransport.cpp | 18 +-- ixwebsocket/IXWebSocketTransport.h | 10 +- 11 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 ixwebsocket/IXWebSocketSendData.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c4fb61a..365421ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ set( IXWEBSOCKET_HEADERS ixwebsocket/IXWebSocketPerMessageDeflateCodec.h ixwebsocket/IXWebSocketPerMessageDeflateOptions.h ixwebsocket/IXWebSocketProxyServer.h + ixwebsocket/IXWebSocketSendData.h ixwebsocket/IXWebSocketSendInfo.h ixwebsocket/IXWebSocketServer.h ixwebsocket/IXWebSocketTransport.h diff --git a/docs/usage.md b/docs/usage.md index ea4116bd..fe63d39f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -90,6 +90,18 @@ auto result = }); ``` +The `send()` and `sendText()` methods check that the string contains only valid UTF-8 characters. If you know that the string is a valid UTF-8 string you can skip that step and use the `sendUtf8Text` method instead. + +With the IXWebSocketSendData overloads of `sendUtf8Text` and `sendBinary` it is possible to not only send std::string but also `std::vector`, `std::vector` and `char*`. + +``` +std::vector data({1, 2, 3, 4}); +auto result = webSocket.sendBinary(data); + +const char* text = "Hello World!"; +result = webSocket.sendUtf8Text(IXWebSocketSendData(text, strlen(text))); +``` + ### ReadyState `getReadyState()` returns the state of the connection. There are 4 possible states. diff --git a/ixwebsocket/IXWebSocket.cpp b/ixwebsocket/IXWebSocket.cpp index e272521a..7aab7902 100644 --- a/ixwebsocket/IXWebSocket.cpp +++ b/ixwebsocket/IXWebSocket.cpp @@ -467,10 +467,28 @@ namespace ix return (binary) ? sendBinary(data, onProgressCallback) : sendText(data, onProgressCallback); } - WebSocketSendInfo WebSocket::sendBinary(const std::string& text, + WebSocketSendInfo WebSocket::sendBinary(const std::string& data, const OnProgressCallback& onProgressCallback) { - return sendMessage(text, SendMessageKind::Binary, onProgressCallback); + return sendMessage(data, SendMessageKind::Binary, onProgressCallback); + } + + WebSocketSendInfo WebSocket::sendBinary(const IXWebSocketSendData& data, + const OnProgressCallback& onProgressCallback) + { + return sendMessage(data, SendMessageKind::Binary, onProgressCallback); + } + + WebSocketSendInfo WebSocket::sendUtf8Text(const std::string& text, + const OnProgressCallback& onProgressCallback) + { + return sendMessage(text, SendMessageKind::Text, onProgressCallback); + } + + WebSocketSendInfo WebSocket::sendUtf8Text(const IXWebSocketSendData& text, + const OnProgressCallback& onProgressCallback) + { + return sendMessage(text, SendMessageKind::Text, onProgressCallback); } WebSocketSendInfo WebSocket::sendText(const std::string& text, @@ -494,7 +512,7 @@ namespace ix return sendMessage(text, SendMessageKind::Ping); } - WebSocketSendInfo WebSocket::sendMessage(const std::string& text, + WebSocketSendInfo WebSocket::sendMessage(const IXWebSocketSendData& message, SendMessageKind sendMessageKind, const OnProgressCallback& onProgressCallback) { @@ -516,19 +534,19 @@ namespace ix { case SendMessageKind::Text: { - webSocketSendInfo = _ws.sendText(text, onProgressCallback); + webSocketSendInfo = _ws.sendText(message, onProgressCallback); } break; case SendMessageKind::Binary: { - webSocketSendInfo = _ws.sendBinary(text, onProgressCallback); + webSocketSendInfo = _ws.sendBinary(message, onProgressCallback); } break; case SendMessageKind::Ping: { - webSocketSendInfo = _ws.sendPing(text); + webSocketSendInfo = _ws.sendPing(message); } break; } diff --git a/ixwebsocket/IXWebSocket.h b/ixwebsocket/IXWebSocket.h index 7cfe0088..37df88ca 100644 --- a/ixwebsocket/IXWebSocket.h +++ b/ixwebsocket/IXWebSocket.h @@ -17,6 +17,7 @@ #include "IXWebSocketMessage.h" #include "IXWebSocketPerMessageDeflateOptions.h" #include "IXWebSocketSendInfo.h" +#include "IXWebSocketSendData.h" #include "IXWebSocketTransport.h" #include #include @@ -75,8 +76,16 @@ namespace ix WebSocketSendInfo send(const std::string& data, bool binary = false, const OnProgressCallback& onProgressCallback = nullptr); - WebSocketSendInfo sendBinary(const std::string& text, + WebSocketSendInfo sendBinary(const std::string& data, const OnProgressCallback& onProgressCallback = nullptr); + WebSocketSendInfo sendBinary(const IXWebSocketSendData& data, + const OnProgressCallback& onProgressCallback = nullptr); + // does not check for valid UTF-8 characters. Caller must check that. + WebSocketSendInfo sendUtf8Text(const std::string& text, + const OnProgressCallback& onProgressCallback = nullptr); + // does not check for valid UTF-8 characters. Caller must check that. + WebSocketSendInfo sendUtf8Text(const IXWebSocketSendData& text, + const OnProgressCallback& onProgressCallback = nullptr); WebSocketSendInfo sendText(const std::string& text, const OnProgressCallback& onProgressCallback = nullptr); WebSocketSendInfo ping(const std::string& text); @@ -107,7 +116,7 @@ namespace ix const std::vector& getSubProtocols(); private: - WebSocketSendInfo sendMessage(const std::string& text, + WebSocketSendInfo sendMessage(const IXWebSocketSendData& message, SendMessageKind sendMessageKind, const OnProgressCallback& callback = nullptr); diff --git a/ixwebsocket/IXWebSocketPerMessageDeflate.cpp b/ixwebsocket/IXWebSocketPerMessageDeflate.cpp index 80435870..1c5f1959 100644 --- a/ixwebsocket/IXWebSocketPerMessageDeflate.cpp +++ b/ixwebsocket/IXWebSocketPerMessageDeflate.cpp @@ -78,6 +78,11 @@ namespace ix _decompressor->init(inflateBits, clientNoContextTakeover); } + bool WebSocketPerMessageDeflate::compress(const IXWebSocketSendData& in, std::string& out) + { + return _compressor->compress(in, out); + } + bool WebSocketPerMessageDeflate::compress(const std::string& in, std::string& out) { return _compressor->compress(in, out); diff --git a/ixwebsocket/IXWebSocketPerMessageDeflate.h b/ixwebsocket/IXWebSocketPerMessageDeflate.h index 9177409d..5d4cc385 100644 --- a/ixwebsocket/IXWebSocketPerMessageDeflate.h +++ b/ixwebsocket/IXWebSocketPerMessageDeflate.h @@ -36,6 +36,7 @@ #include #include +#include "IXWebSocketSendData.h" namespace ix { @@ -50,6 +51,7 @@ namespace ix ~WebSocketPerMessageDeflate(); bool init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions); + bool compress(const IXWebSocketSendData& in, std::string& out); bool compress(const std::string& in, std::string& out); bool decompress(const std::string& in, std::string& out); diff --git a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp b/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp index d641c47c..0a52d4d4 100644 --- a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp +++ b/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp @@ -78,6 +78,12 @@ namespace ix return compressData(in, out); } + bool WebSocketPerMessageDeflateCompressor::compress(const IXWebSocketSendData& in, + std::string& out) + { + return compressData(in, out); + } + bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, std::vector& out) { diff --git a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.h b/ixwebsocket/IXWebSocketPerMessageDeflateCodec.h index 2d06517c..8c0d6b1b 100644 --- a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.h +++ b/ixwebsocket/IXWebSocketPerMessageDeflateCodec.h @@ -12,6 +12,7 @@ #include #include #include +#include "IXWebSocketSendData.h" namespace ix { @@ -22,6 +23,7 @@ namespace ix ~WebSocketPerMessageDeflateCompressor(); bool init(uint8_t deflateBits, bool clientNoContextTakeOver); + bool compress(const IXWebSocketSendData& in, std::string& out); bool compress(const std::string& in, std::string& out); bool compress(const std::string& in, std::vector& out); bool compress(const std::vector& in, std::string& out); diff --git a/ixwebsocket/IXWebSocketSendData.h b/ixwebsocket/IXWebSocketSendData.h new file mode 100644 index 00000000..86031aa0 --- /dev/null +++ b/ixwebsocket/IXWebSocketSendData.h @@ -0,0 +1,128 @@ +/* + * IXWebSocketSendData.h + * + * WebSocket (Binary/Text) send data buffer + */ + +#pragma once + +#include +#include +#include + +namespace ix +{ + /* + * IXWebSocketSendData implements a wrapper for std::string, std:vector and char*. + * It removes the necessarity to copy the data or string into a std::string + */ + class IXWebSocketSendData { + public: + + template + struct IXWebSocketSendData_const_iterator + //: public std::iterator + { + typedef IXWebSocketSendData_const_iterator const_iterator; + + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = value_type*; + using reference = const value_type&; + + pointer _ptr; + public: + IXWebSocketSendData_const_iterator() : _ptr(nullptr) {} + IXWebSocketSendData_const_iterator(pointer ptr) : _ptr(ptr) {} + ~IXWebSocketSendData_const_iterator() {} + + const_iterator operator++(int) { return const_iterator(_ptr++); } + const_iterator& operator++() { ++_ptr; return *this; } + reference operator* () const { return *_ptr; } + pointer operator->() const { return _ptr; } + const_iterator operator+ (const difference_type offset) const { return const_iterator(_ptr + offset); } + const_iterator operator- (const difference_type offset) const { return const_iterator(_ptr - offset); } + difference_type operator- (const const_iterator& rhs) const { return _ptr - rhs._ptr; } + bool operator==(const const_iterator& rhs) const { return _ptr == rhs._ptr; } + bool operator!=(const const_iterator& rhs) const { return _ptr != rhs._ptr; } + const_iterator& operator+=(const difference_type offset) { _ptr += offset; return *this; } + const_iterator& operator-=(const difference_type offset) { _ptr -= offset; return *this; } + }; + + using const_iterator = IXWebSocketSendData_const_iterator; + + /* The assigned std::string must be kept alive for the lifetime of the input buffer */ + IXWebSocketSendData(const std::string& str) + : _data(str.data()) + , _size(str.size()) + { + } + + /* The assigned std::vector must be kept alive for the lifetime of the input buffer */ + IXWebSocketSendData(const std::vector& v) + : _data(v.data()) + , _size(v.size()) + { + } + + /* The assigned std::vector must be kept alive for the lifetime of the input buffer */ + IXWebSocketSendData(const std::vector& v) + : _data(reinterpret_cast(v.data())) + , _size(v.size()) + { + } + + /* The assigned memory must be kept alive for the lifetime of the input buffer */ + IXWebSocketSendData(const char* data, size_t size) + : _data(data) + , _size(data == nullptr ? 0 : size) + { + } + + bool empty() const + { + return _data == nullptr || _size == 0; + } + + const char* c_str() const + { + return _data; + } + + const char* data() const + { + return _data; + } + + size_t size() const + { + return _size; + } + + inline const_iterator begin() const + { + return const_iterator(const_cast(_data)); + } + + inline const_iterator end() const + { + return const_iterator(const_cast(_data) + _size); + } + + inline const_iterator cbegin() const + { + return begin(); + } + + inline const_iterator cend() const + { + return end(); + } + + private: + const char* _data; + const size_t _size; + }; + +} \ No newline at end of file diff --git a/ixwebsocket/IXWebSocketTransport.cpp b/ixwebsocket/IXWebSocketTransport.cpp index 1fcdfdeb..86ec52e8 100644 --- a/ixwebsocket/IXWebSocketTransport.cpp +++ b/ixwebsocket/IXWebSocketTransport.cpp @@ -776,9 +776,8 @@ namespace ix return static_cast(seconds); } - template WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type, - const T& message, + const IXWebSocketSendData& message, bool compress, const OnProgressCallback& onProgressCallback) { @@ -807,8 +806,9 @@ namespace ix compressionError = false; wireSize = _compressedMessage.size(); - message_begin = _compressedMessage.cbegin(); - message_end = _compressedMessage.cend(); + IXWebSocketSendData compressedSendData(_compressedMessage); + message_begin = compressedSendData.cbegin(); + message_end = compressedSendData.cend(); } { @@ -840,8 +840,8 @@ namespace ix // auto steps = wireSize / kChunkSize; - std::string::const_iterator begin = message_begin; - std::string::const_iterator end = message_end; + auto begin = message_begin; + auto end = message_end; for (uint64_t i = 0; i < steps; ++i) { @@ -980,7 +980,7 @@ namespace ix return sendOnSocket(); } - WebSocketSendInfo WebSocketTransport::sendPing(const std::string& message) + WebSocketSendInfo WebSocketTransport::sendPing(const IXWebSocketSendData& message) { bool compress = false; WebSocketSendInfo info = sendData(wsheader_type::PING, message, compress); @@ -994,7 +994,7 @@ namespace ix return info; } - WebSocketSendInfo WebSocketTransport::sendBinary(const std::string& message, + WebSocketSendInfo WebSocketTransport::sendBinary(const IXWebSocketSendData& message, const OnProgressCallback& onProgressCallback) { @@ -1002,7 +1002,7 @@ namespace ix wsheader_type::BINARY_FRAME, message, _enablePerMessageDeflate, onProgressCallback); } - WebSocketSendInfo WebSocketTransport::sendText(const std::string& message, + WebSocketSendInfo WebSocketTransport::sendText(const IXWebSocketSendData& message, const OnProgressCallback& onProgressCallback) { diff --git a/ixwebsocket/IXWebSocketTransport.h b/ixwebsocket/IXWebSocketTransport.h index 777e29c0..bdfd409f 100644 --- a/ixwebsocket/IXWebSocketTransport.h +++ b/ixwebsocket/IXWebSocketTransport.h @@ -19,6 +19,7 @@ #include "IXWebSocketPerMessageDeflate.h" #include "IXWebSocketPerMessageDeflateOptions.h" #include "IXWebSocketSendInfo.h" +#include "IXWebSocketSendData.h" #include #include #include @@ -88,11 +89,11 @@ namespace ix bool enablePerMessageDeflate); PollResult poll(); - WebSocketSendInfo sendBinary(const std::string& message, + WebSocketSendInfo sendBinary(const IXWebSocketSendData& message, const OnProgressCallback& onProgressCallback); - WebSocketSendInfo sendText(const std::string& message, + WebSocketSendInfo sendText(const IXWebSocketSendData& message, const OnProgressCallback& onProgressCallback); - WebSocketSendInfo sendPing(const std::string& message); + WebSocketSendInfo sendPing(const IXWebSocketSendData& message); void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage, @@ -241,9 +242,8 @@ namespace ix bool sendOnSocket(); bool receiveFromSocket(); - template WebSocketSendInfo sendData(wsheader_type::opcode_type type, - const T& message, + const IXWebSocketSendData& message, bool compress, const OnProgressCallback& onProgressCallback = nullptr);