tweaks doc / license + send proper error code when closing the connecion
This commit is contained in:
parent
43fcf93584
commit
a7a422d6ed
@ -1,5 +1,4 @@
|
|||||||
#
|
#
|
||||||
# cmd_websocket_chat.cpp
|
|
||||||
# Author: Benjamin Sergeant
|
# Author: Benjamin Sergeant
|
||||||
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
#
|
#
|
||||||
|
@ -51,7 +51,7 @@ int main(int argc, char* argv[])
|
|||||||
bool done = false;
|
bool done = false;
|
||||||
ix::SatoriConnection satoriConnection;
|
ix::SatoriConnection satoriConnection;
|
||||||
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
|
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
|
||||||
false, false, false, 15, 15);
|
true, false, false, 15, 15);
|
||||||
satoriConnection.configure(appkey, endpoint, rolename, rolesecret,
|
satoriConnection.configure(appkey, endpoint, rolename, rolesecret,
|
||||||
webSocketPerMessageDeflateOptions);
|
webSocketPerMessageDeflateOptions);
|
||||||
satoriConnection.connect();
|
satoriConnection.connect();
|
||||||
|
@ -49,6 +49,10 @@ namespace
|
|||||||
{
|
{
|
||||||
_webSocket.setUrl(_url);
|
_webSocket.setUrl(_url);
|
||||||
|
|
||||||
|
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
|
||||||
|
true, false, false, 15, 15);
|
||||||
|
_webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
log(std::string("Connecting to url: ") + _url);
|
log(std::string("Connecting to url: ") + _url);
|
||||||
|
|
||||||
@ -64,6 +68,11 @@ namespace
|
|||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocket_MessageType_Open)
|
||||||
{
|
{
|
||||||
log("ws_connect: connected");
|
log("ws_connect: connected");
|
||||||
|
std::cout << "Handshake Headers:" << std::endl;
|
||||||
|
for (auto it : headers)
|
||||||
|
{
|
||||||
|
std::cout << it.first << ": " << it.second << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,48 @@
|
|||||||
/*
|
/*
|
||||||
* IXWebSocketPerMessageDeflate.cpp
|
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the WebSocket++ Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Per message Deflate RFC: https://tools.ietf.org/html/rfc7692
|
* Adapted from websocketpp/extensions/permessage_deflate/enabled.hpp
|
||||||
|
* (same license as MZ: https://opensource.org/licenses/BSD-3-Clause)
|
||||||
*
|
*
|
||||||
|
* - Reused zlib compression + decompression bits.
|
||||||
|
* - Refactored to have 2 class for compression and decompression, to allow multi-threading
|
||||||
|
* and make sure that _compressBuffer is not shared between threads.
|
||||||
|
* - Original code wasn't working for some reason, I had to add checks
|
||||||
|
* for the presence of the kEmptyUncompressedBlock at the end of buffer so that servers
|
||||||
|
* would start accepting receiving/decoding compressed messages. Original code was probably
|
||||||
|
* modifying the passed in buffers before processing in enabled.hpp ?
|
||||||
|
* - Added more documentation.
|
||||||
|
*
|
||||||
|
* Per message Deflate RFC: https://tools.ietf.org/html/rfc7692
|
||||||
* Chrome websocket -> https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets
|
* Chrome websocket -> https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXWebSocketPerMessageDeflate.h"
|
#include "IXWebSocketPerMessageDeflate.h"
|
||||||
@ -43,7 +80,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
|
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
|
||||||
bool client_no_context_takeover)
|
bool clientNoContextTakeOver)
|
||||||
{
|
{
|
||||||
int ret = deflateInit2(
|
int ret = deflateInit2(
|
||||||
&_deflateState,
|
&_deflateState,
|
||||||
@ -57,14 +94,9 @@ namespace ix
|
|||||||
if (ret != Z_OK) return false;
|
if (ret != Z_OK) return false;
|
||||||
|
|
||||||
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
|
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
|
||||||
if (client_no_context_takeover)
|
_flush = (clientNoContextTakeOver)
|
||||||
{
|
? Z_FULL_FLUSH
|
||||||
_flush = Z_FULL_FLUSH;
|
: Z_SYNC_FLUSH;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_flush = Z_SYNC_FLUSH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -79,6 +111,23 @@ namespace ix
|
|||||||
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in,
|
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in,
|
||||||
std::string& out)
|
std::string& out)
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// 7.2.1. Compression
|
||||||
|
//
|
||||||
|
// An endpoint uses the following algorithm to compress a message.
|
||||||
|
//
|
||||||
|
// 1. Compress all the octets of the payload of the message using
|
||||||
|
// DEFLATE.
|
||||||
|
//
|
||||||
|
// 2. If the resulting data does not end with an empty DEFLATE block
|
||||||
|
// with no compression (the "BTYPE" bits are set to 00), append an
|
||||||
|
// empty DEFLATE block with no compression to the tail end.
|
||||||
|
//
|
||||||
|
// 3. Remove 4 octets (that are 0x00 0x00 0xff 0xff) from the tail end.
|
||||||
|
// After this step, the last octet of the compressed data contains
|
||||||
|
// (possibly part of) the DEFLATE header bits with the "BTYPE" bits
|
||||||
|
// set to 00.
|
||||||
|
//
|
||||||
size_t output;
|
size_t output;
|
||||||
|
|
||||||
if (in.empty())
|
if (in.empty())
|
||||||
@ -131,7 +180,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
|
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
|
||||||
bool client_no_context_takeover)
|
bool clientNoContextTakeOver)
|
||||||
{
|
{
|
||||||
int ret = inflateInit2(
|
int ret = inflateInit2(
|
||||||
&_inflateState,
|
&_inflateState,
|
||||||
@ -141,14 +190,9 @@ namespace ix
|
|||||||
if (ret != Z_OK) return false;
|
if (ret != Z_OK) return false;
|
||||||
|
|
||||||
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
|
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
|
||||||
if (client_no_context_takeover)
|
_flush = (clientNoContextTakeOver)
|
||||||
{
|
? Z_FULL_FLUSH
|
||||||
_flush = Z_FULL_FLUSH;
|
: Z_SYNC_FLUSH;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_flush = Z_SYNC_FLUSH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -156,6 +200,16 @@ namespace ix
|
|||||||
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in,
|
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in,
|
||||||
std::string& out)
|
std::string& out)
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// 7.2.2. Decompression
|
||||||
|
//
|
||||||
|
// An endpoint uses the following algorithm to decompress a message.
|
||||||
|
//
|
||||||
|
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
||||||
|
// payload of the message.
|
||||||
|
//
|
||||||
|
// 2. Decompress the resulting data using DEFLATE.
|
||||||
|
//
|
||||||
std::string inFixed(in);
|
std::string inFixed(in);
|
||||||
inFixed += kEmptyUncompressedBlock;
|
inFixed += kEmptyUncompressedBlock;
|
||||||
|
|
||||||
|
@ -1,7 +1,35 @@
|
|||||||
/*
|
/*
|
||||||
* IXWebSocketPerMessageDeflate.h
|
* Copyright (c) 2015, Peter Thorson. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the WebSocket++ Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Adapted from websocketpp/extensions/permessage_deflate/enabled.hpp
|
||||||
|
* (same license as MZ: https://opensource.org/licenses/BSD-3-Clause)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -18,7 +46,8 @@ namespace ix
|
|||||||
public:
|
public:
|
||||||
WebSocketPerMessageDeflateCompressor();
|
WebSocketPerMessageDeflateCompressor();
|
||||||
~WebSocketPerMessageDeflateCompressor();
|
~WebSocketPerMessageDeflateCompressor();
|
||||||
bool init(uint8_t deflate_bits, bool client_no_context_takeover);
|
|
||||||
|
bool init(uint8_t deflateBits, bool clientNoContextTakeOver);
|
||||||
bool compress(const std::string& in, std::string& out);
|
bool compress(const std::string& in, std::string& out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -27,7 +56,6 @@ namespace ix
|
|||||||
int _flush;
|
int _flush;
|
||||||
size_t _compressBufferSize;
|
size_t _compressBufferSize;
|
||||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||||
|
|
||||||
z_stream _deflateState;
|
z_stream _deflateState;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,14 +64,14 @@ namespace ix
|
|||||||
public:
|
public:
|
||||||
WebSocketPerMessageDeflateDecompressor();
|
WebSocketPerMessageDeflateDecompressor();
|
||||||
~WebSocketPerMessageDeflateDecompressor();
|
~WebSocketPerMessageDeflateDecompressor();
|
||||||
bool init(uint8_t inflate_bits, bool client_no_context_takeover);
|
|
||||||
|
bool init(uint8_t inflateBits, bool clientNoContextTakeOver);
|
||||||
bool decompress(const std::string& in, std::string& out);
|
bool decompress(const std::string& in, std::string& out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _flush;
|
int _flush;
|
||||||
size_t _compressBufferSize;
|
size_t _compressBufferSize;
|
||||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||||
|
|
||||||
z_stream _inflateState;
|
z_stream _inflateState;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,17 +79,13 @@ namespace ix
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebSocketPerMessageDeflate();
|
WebSocketPerMessageDeflate();
|
||||||
virtual ~WebSocketPerMessageDeflate();
|
~WebSocketPerMessageDeflate();
|
||||||
|
|
||||||
bool init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
|
bool init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
|
||||||
|
|
||||||
bool compress(const std::string& in, std::string& out);
|
bool compress(const std::string& in, std::string& out);
|
||||||
bool decompress(const std::string& in, std::string& out);
|
bool decompress(const std::string& in, std::string& out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mode::value m_server_max_window_bits_mode;
|
|
||||||
// mode::value m_client_max_window_bits_mode;
|
|
||||||
|
|
||||||
std::shared_ptr<WebSocketPerMessageDeflateCompressor> _compressor;
|
std::shared_ptr<WebSocketPerMessageDeflateCompressor> _compressor;
|
||||||
std::shared_ptr<WebSocketPerMessageDeflateDecompressor> _decompressor;
|
std::shared_ptr<WebSocketPerMessageDeflateDecompressor> _decompressor;
|
||||||
};
|
};
|
||||||
|
@ -578,7 +578,7 @@ namespace ix
|
|||||||
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
||||||
|
|
||||||
// Reply back right away
|
// Reply back right away
|
||||||
sendData(wsheader_type::PONG, pingData);
|
sendData(wsheader_type::PONG, pingData, _enablePerMessageDeflate);
|
||||||
|
|
||||||
emitMessage(PING, pingData, ws, onMessageCallback);
|
emitMessage(PING, pingData, ws, onMessageCallback);
|
||||||
}
|
}
|
||||||
@ -658,7 +658,8 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message)
|
const std::string& message,
|
||||||
|
bool compress)
|
||||||
{
|
{
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED)
|
if (_readyState == CLOSING || _readyState == CLOSED)
|
||||||
{
|
{
|
||||||
@ -672,7 +673,7 @@ namespace ix
|
|||||||
std::string::const_iterator message_begin = message.begin();
|
std::string::const_iterator message_begin = message.begin();
|
||||||
std::string::const_iterator message_end = message.end();
|
std::string::const_iterator message_end = message.end();
|
||||||
|
|
||||||
if (_enablePerMessageDeflate)
|
if (compress)
|
||||||
{
|
{
|
||||||
_perMessageDeflate.compress(message, compressedMessage);
|
_perMessageDeflate.compress(message, compressedMessage);
|
||||||
wireSize = compressedMessage.size();
|
wireSize = compressedMessage.size();
|
||||||
@ -697,7 +698,7 @@ namespace ix
|
|||||||
header[0] = 0x80 | type;
|
header[0] = 0x80 | type;
|
||||||
|
|
||||||
// This bit indicate that the frame is compressed
|
// This bit indicate that the frame is compressed
|
||||||
if (_enablePerMessageDeflate)
|
if (compress)
|
||||||
{
|
{
|
||||||
header[0] |= 0x40;
|
header[0] |= 0x40;
|
||||||
}
|
}
|
||||||
@ -752,12 +753,12 @@ namespace ix
|
|||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendPing(const std::string& message)
|
WebSocketSendInfo WebSocketTransport::sendPing(const std::string& message)
|
||||||
{
|
{
|
||||||
return sendData(wsheader_type::PING, message);
|
return sendData(wsheader_type::PING, message, _enablePerMessageDeflate);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendBinary(const std::string& message)
|
WebSocketSendInfo WebSocketTransport::sendBinary(const std::string& message)
|
||||||
{
|
{
|
||||||
return sendData(wsheader_type::BINARY_FRAME, message);
|
return sendData(wsheader_type::BINARY_FRAME, message, _enablePerMessageDeflate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::sendOnSocket()
|
void WebSocketTransport::sendOnSocket()
|
||||||
@ -791,12 +792,17 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED) return;
|
if (_readyState == CLOSING || _readyState == CLOSED) return;
|
||||||
|
|
||||||
|
// See list of close events here:
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||||
|
// We use 1000: normal closure.
|
||||||
|
//
|
||||||
|
// >>> struct.pack('!H', 1000)
|
||||||
|
// b'\x03\xe8'
|
||||||
|
//
|
||||||
|
const std::string normalClosure = std::string("\x03\xe9");
|
||||||
|
bool compress = false;
|
||||||
|
sendData(wsheader_type::CLOSE, normalClosure, compress);
|
||||||
setReadyState(CLOSING);
|
setReadyState(CLOSING);
|
||||||
uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00}; // last 4 bytes are a masking key
|
|
||||||
std::vector<uint8_t> header(closeFrame, closeFrame+6);
|
|
||||||
appendToSendBuffer(header);
|
|
||||||
|
|
||||||
sendOnSocket();
|
|
||||||
|
|
||||||
_socket->wakeUpFromPoll();
|
_socket->wakeUpFromPoll();
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
@ -146,7 +146,8 @@ namespace ix
|
|||||||
|
|
||||||
void sendOnSocket();
|
void sendOnSocket();
|
||||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message);
|
const std::string& message,
|
||||||
|
bool compress);
|
||||||
|
|
||||||
void emitMessage(MessageKind messageKind,
|
void emitMessage(MessageKind messageKind,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
|
Loading…
Reference in New Issue
Block a user