IXWebSocket/ixwebsocket/IXWebSocketPerMessageDeflate.cpp
2018-11-09 18:42:09 -08:00

223 lines
6.4 KiB
C++

/*
* IXWebSocketPerMessageDeflate.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*
* Per message Deflate RFC: https://tools.ietf.org/html/rfc7692
*
* Chrome websocket -> https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets
*/
#include "IXWebSocketPerMessageDeflate.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include <iostream>
#include <cassert>
namespace
{
// The passed in size (4) is important, without it the string litteral
// is treated as a char* and the null termination (\x00) makes it
// look like an empty string.
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
const int kBufferSize = 1 << 14;
}
namespace ix
{
//
// Compressor
//
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
: _compressBufferSize(kBufferSize)
{
_deflateState.zalloc = Z_NULL;
_deflateState.zfree = Z_NULL;
_deflateState.opaque = Z_NULL;
}
WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
{
deflateEnd(&_deflateState);
}
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
bool client_no_context_takeover)
{
int ret = deflateInit2(
&_deflateState,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
-1*deflateBits,
4, // memory level 1-9
Z_DEFAULT_STRATEGY
);
if (ret != Z_OK) return false;
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
if (client_no_context_takeover)
{
_flush = Z_FULL_FLUSH;
}
else
{
_flush = Z_SYNC_FLUSH;
}
return true;
}
bool WebSocketPerMessageDeflateCompressor::endsWith(const std::string& value,
const std::string& ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in,
std::string& out)
{
size_t output;
if (in.empty())
{
uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
out.append((char *)(buf), 6);
return true;
}
_deflateState.avail_in = (uInt) in.size();
_deflateState.next_in = (Bytef*) in.data();
do
{
// Output to local buffer
_deflateState.avail_out = (uInt) _compressBufferSize;
_deflateState.next_out = _compressBuffer.get();
deflate(&_deflateState, _flush);
output = _compressBufferSize - _deflateState.avail_out;
out.append((char *)(_compressBuffer.get()),output);
} while (_deflateState.avail_out == 0);
if (endsWith(out, kEmptyUncompressedBlock))
{
out.resize(out.size() - 4);
}
return true;
}
//
// Decompressor
//
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
: _compressBufferSize(kBufferSize)
{
_inflateState.zalloc = Z_NULL;
_inflateState.zfree = Z_NULL;
_inflateState.opaque = Z_NULL;
_inflateState.avail_in = 0;
_inflateState.next_in = Z_NULL;
}
WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
{
inflateEnd(&_inflateState);
}
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
bool client_no_context_takeover)
{
int ret = inflateInit2(
&_inflateState,
-1*inflateBits
);
if (ret != Z_OK) return false;
_compressBuffer.reset(new unsigned char[_compressBufferSize]);
if (client_no_context_takeover)
{
_flush = Z_FULL_FLUSH;
}
else
{
_flush = Z_SYNC_FLUSH;
}
return true;
}
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in,
std::string& out)
{
std::string inFixed(in);
inFixed += kEmptyUncompressedBlock;
_inflateState.avail_in = (uInt) inFixed.size();
_inflateState.next_in = (unsigned char *)(const_cast<char *>(inFixed.data()));
do
{
_inflateState.avail_out = (uInt) _compressBufferSize;
_inflateState.next_out = _compressBuffer.get();
int ret = inflate(&_inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
return false; // zlib error
}
out.append(
reinterpret_cast<char *>(_compressBuffer.get()),
_compressBufferSize - _inflateState.avail_out
);
} while (_inflateState.avail_out == 0);
return true;
}
WebSocketPerMessageDeflate::WebSocketPerMessageDeflate()
{
_compressor.reset(new WebSocketPerMessageDeflateCompressor());
_decompressor.reset(new WebSocketPerMessageDeflateDecompressor());
}
WebSocketPerMessageDeflate::~WebSocketPerMessageDeflate()
{
_compressor.reset();
_decompressor.reset();
}
bool WebSocketPerMessageDeflate::init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
{
bool clientNoContextTakeover =
perMessageDeflateOptions.getClientNoContextTakeover();
uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits();
uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits();
return _compressor->init(deflateBits, clientNoContextTakeover) &&
_decompressor->init(inflateBits, clientNoContextTakeover);
}
bool WebSocketPerMessageDeflate::compress(const std::string& in,
std::string& out)
{
return _compressor->compress(in, out);
}
bool WebSocketPerMessageDeflate::decompress(const std::string& in,
std::string &out)
{
return _decompressor->decompress(in, out);
}
}