Skip to content

Commit 31594f1

Browse files
split out zlib stuff to its own file
1 parent 3119752 commit 31594f1

File tree

4 files changed

+173
-107
lines changed

4 files changed

+173
-107
lines changed

include/dpp/discordclient.h

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <dpp/etf.h>
3737
#include <mutex>
3838
#include <shared_mutex>
39+
#include <dpp/zlibcontext.h>
3940

4041
namespace dpp {
4142

@@ -52,18 +53,6 @@ namespace dpp {
5253
/* Forward declarations */
5354
class cluster;
5455

55-
/**
56-
* @brief This is an opaque class containing zlib library specific structures.
57-
* We define it this way so that the public facing D++ library doesn't require
58-
* the zlib headers be available to build against it.
59-
*/
60-
class zlibcontext;
61-
62-
/**
63-
* @brief Size of decompression buffer for zlib compressed traffic
64-
*/
65-
constexpr size_t DECOMP_BUFFER_SIZE = 512 * 1024;
66-
6756
/**
6857
* @brief How many seconds to wait between (re)connections. DO NOT change this.
6958
* It is mandated by the Discord API spec!
@@ -308,15 +297,6 @@ class DPP_EXPORT discord_client : public websocket_client
308297
*/
309298
bool compressed;
310299

311-
/**
312-
* @brief ZLib decompression buffer
313-
*
314-
* If compression is not in use, this remains set to
315-
* a vector of length zero, but when compression is
316-
* enabled it will be resized to a DECOMP_BUFFER_SIZE buffer.
317-
*/
318-
std::vector<unsigned char> decomp_buffer;
319-
320300
/**
321301
* @brief Decompressed string
322302
*/
@@ -330,11 +310,6 @@ class DPP_EXPORT discord_client : public websocket_client
330310
*/
331311
std::unique_ptr<zlibcontext> zlib{};
332312

333-
/**
334-
* @brief Total decompressed received bytes
335-
*/
336-
uint64_t decompressed_total;
337-
338313
/**
339314
* @brief Last connect time of cluster
340315
*/
@@ -573,11 +548,13 @@ class DPP_EXPORT discord_client : public websocket_client
573548
/**
574549
* @brief Destroy the discord client object
575550
*/
576-
virtual ~discord_client() override;
551+
virtual ~discord_client() = default;
577552

578553
/**
579-
* @brief Get the decompressed bytes in objectGet decompressed total bytes received
580-
* @return uint64_t bytes received
554+
* @brief Get decompressed total bytes received
555+
*
556+
* This will always return 0 if the connection is not compressed
557+
* @return uint64_t compressed bytes received
581558
*/
582559
uint64_t get_decompressed_bytes_in();
583560

include/dpp/zlibcontext.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/************************************************************************************
2+
*
3+
* D++, A Lightweight C++ library for Discord
4+
*
5+
* Copyright 2021 Craig Edwards and D++ contributors
6+
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
************************************************************************************/
21+
#pragma once
22+
#include <dpp/export.h>
23+
#include <dpp/exception.h>
24+
#include <cstdint>
25+
#include <vector>
26+
#include <memory>
27+
28+
/**
29+
* @brief Forward declaration for zlib stream type
30+
*/
31+
typedef struct z_stream_s z_stream;
32+
33+
namespace dpp {
34+
35+
/**
36+
* @brief Size of decompression buffer for zlib compressed traffic
37+
*/
38+
constexpr size_t DECOMP_BUFFER_SIZE = 512 * 1024;
39+
40+
/**
41+
* @brief This is an opaque class containing zlib library specific structures.
42+
* This wraps the C pointers needed for zlib with unique_ptr and gives us a nice
43+
* buffer abstraction so we don't need to wrestle with raw pointers.
44+
*/
45+
class zlibcontext {
46+
public:
47+
/**
48+
* @brief Zlib stream struct. The actual type is defined in zlib.h
49+
* so is only defined in the implementation file.
50+
*/
51+
std::unique_ptr<z_stream> d_stream{};
52+
53+
/**
54+
* @brief ZLib decompression buffer.
55+
* This is automatically set to DECOMP_BUFFER_SIZE bytes when
56+
* the class is constructed.
57+
*/
58+
std::vector<unsigned char> decomp_buffer{};
59+
60+
/**
61+
* @brief Total decompressed received bytes counter
62+
*/
63+
uint64_t decompressed_total{};
64+
65+
/**
66+
* @brief Initialise zlib struct via inflateInit()
67+
* and size the buffer
68+
*/
69+
zlibcontext();
70+
71+
/**
72+
* @brief Destroy zlib struct via inflateEnd()
73+
*/
74+
~zlibcontext();
75+
76+
/**
77+
* @brief Decompress zlib deflated buffer
78+
* @param buffer input compressed stream
79+
* @param decompressed output decompressed content
80+
* @return an error code on error, or err_no_code_specified (0) on success
81+
*/
82+
exception_error_code decompress(const std::string& buffer, std::string& decompressed);
83+
};
84+
85+
}

src/dpp/discordclient.cpp

Lines changed: 9 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* D++, A Lightweight C++ library for Discord
44
*
55
* SPDX-License-Identifier: Apache-2.0
6-
* Copyright 2021 Craig Edwards and D++ contributors
6+
#include <dpp/zlibcontext.h> * Copyright 2021 Craig Edwards and D++ contributors
77
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
88
*
99
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,6 @@
2828
#include <thread>
2929
#include <dpp/json.h>
3030
#include <dpp/etf.h>
31-
#include <zlib.h>
3231

3332
#define PATH_UNCOMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json"
3433
#define PATH_COMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json&compress=zlib-stream"
@@ -48,45 +47,12 @@ namespace dpp {
4847
*/
4948
constexpr int LARGE_THRESHOLD = 250;
5049

51-
/**
52-
* @brief This is an opaque class containing zlib library specific structures.
53-
* We define it this way so that the public facing D++ library doesn't require
54-
* the zlib headers be available to build against it.
55-
*/
56-
class zlibcontext {
57-
public:
58-
/**
59-
* @brief Zlib stream struct
60-
*/
61-
z_stream d_stream{};
62-
63-
/**
64-
* @brief Initialise zlib struct via inflateInit()
65-
*/
66-
zlibcontext() {
67-
int error = inflateInit(&d_stream);
68-
if (error != Z_OK) {
69-
throw dpp::connection_exception((exception_error_code)error, "Can't initialise stream compression!");
70-
}
71-
}
72-
73-
/**
74-
* @brief Destroy zlib struct via inflateEnd()
75-
*/
76-
~zlibcontext() {
77-
inflateEnd(&d_stream);
78-
}
79-
};
80-
81-
8250
/**
8351
* @brief Resume constructor for websocket client
8452
*/
8553
discord_client::discord_client(discord_client &old, uint64_t sequence, const std::string& session_id)
8654
: websocket_client(old.owner, old.resume_gateway_url, "443", old.compressed ? (old.protocol == ws_json ? PATH_COMPRESSED_JSON : PATH_COMPRESSED_ETF) : (old.protocol == ws_json ? PATH_UNCOMPRESSED_JSON : PATH_UNCOMPRESSED_ETF)),
8755
compressed(old.compressed),
88-
zlib(nullptr),
89-
decompressed_total(old.decompressed_total),
9056
connect_time(0),
9157
ping_start(0.0),
9258
etf(nullptr),
@@ -113,8 +79,6 @@ discord_client::discord_client(discord_client &old, uint64_t sequence, const std
11379
discord_client::discord_client(dpp::cluster* _cluster, uint32_t _shard_id, uint32_t _max_shards, const std::string &_token, uint32_t _intents, bool comp, websocket_protocol_t ws_proto)
11480
: websocket_client(_cluster, _cluster->default_gateway, "443", comp ? (ws_proto == ws_json ? PATH_COMPRESSED_JSON : PATH_COMPRESSED_ETF) : (ws_proto == ws_json ? PATH_UNCOMPRESSED_JSON : PATH_UNCOMPRESSED_ETF)),
11581
compressed(comp),
116-
zlib(nullptr),
117-
decompressed_total(0),
11882
connect_time(0),
11983
ping_start(0.0),
12084
etf(nullptr),
@@ -138,10 +102,9 @@ discord_client::discord_client(dpp::cluster* _cluster, uint32_t _shard_id, uint3
138102
}
139103

140104
void discord_client::start_connecting() {
141-
etf = std::make_unique<etf_parser>(etf_parser());
105+
etf = std::make_unique<etf_parser>();
142106
if (compressed) {
143107
zlib = std::make_unique<zlibcontext>();
144-
decomp_buffer.resize(DECOMP_BUFFER_SIZE);
145108
}
146109
websocket_client::connect();
147110
}
@@ -150,10 +113,6 @@ void discord_client::cleanup()
150113
{
151114
}
152115

153-
discord_client::~discord_client()
154-
{
155-
}
156-
157116
void discord_client::on_disconnect()
158117
{
159118
log(ll_trace, "discord_client::on_disconnect()");
@@ -167,7 +126,7 @@ void discord_client::on_disconnect()
167126

168127
uint64_t discord_client::get_decompressed_bytes_in()
169128
{
170-
return decompressed_total;
129+
return zlib ? zlib->decompressed_total : 0;
171130
}
172131

173132
void discord_client::set_resume_hostname()
@@ -191,40 +150,12 @@ bool discord_client::handle_frame(const std::string &buffer, ws_opcode opcode)
191150
/* Check that we have a complete compressed frame */
192151
if ((uint8_t)buffer[buffer.size() - 4] == 0x00 && (uint8_t)buffer[buffer.size() - 3] == 0x00 && (uint8_t)buffer[buffer.size() - 2] == 0xFF
193152
&& (uint8_t)buffer[buffer.size() - 1] == 0xFF) {
194-
/* Decompress buffer */
195-
decompressed.clear();
196-
/* This is safe; zlib requires us to cast away the const. The underlying buffer is unchanged. */
197-
zlib->d_stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(buffer.data()));
198-
zlib->d_stream.avail_in = static_cast<uInt>(buffer.size());
199-
do {
200-
zlib->d_stream.next_out = static_cast<Bytef*>(decomp_buffer.data());
201-
zlib->d_stream.avail_out = DECOMP_BUFFER_SIZE;
202-
int ret = inflate(&(zlib->d_stream), Z_NO_FLUSH);
203-
size_t have = DECOMP_BUFFER_SIZE - zlib->d_stream.avail_out;
204-
switch (ret)
205-
{
206-
case Z_NEED_DICT:
207-
case Z_STREAM_ERROR:
208-
this->error(err_compression_stream);
209-
this->close();
210-
return false;
211-
case Z_DATA_ERROR:
212-
this->error(err_compression_data);
213-
this->close();
214-
return false;
215-
case Z_MEM_ERROR:
216-
this->error(err_compression_memory);
217-
this->close();
218-
return false;
219-
case Z_OK:
220-
this->decompressed.append(decomp_buffer.begin(), decomp_buffer.begin() + have);
221-
this->decompressed_total += have;
222-
break;
223-
default:
224-
/* Stub */
225-
break;
226-
}
227-
} while (zlib->d_stream.avail_out == 0);
153+
auto result = zlib->decompress(buffer, decompressed);
154+
if (result != err_no_code_specified) {
155+
this->error(result);
156+
this->close();
157+
return false;
158+
}
228159
data = decompressed;
229160
} else {
230161
/* No complete compressed frame yet */

src/dpp/zlibcontext.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/************************************************************************************
2+
*
3+
* D++, A Lightweight C++ library for Discord
4+
*
5+
* Copyright 2021 Craig Edwards and D++ contributors
6+
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
************************************************************************************/
21+
#include <zlib.h>
22+
#include <memory>
23+
#include <cstring>
24+
#include <dpp/zlibcontext.h>
25+
26+
namespace dpp {
27+
28+
zlibcontext::zlibcontext() {
29+
d_stream = std::make_unique<z_stream>();
30+
std::memset(d_stream.get(), 0, sizeof(z_stream));
31+
int error = inflateInit(d_stream.get());
32+
if (error != Z_OK) {
33+
throw dpp::connection_exception((exception_error_code)error, "Can't initialise stream compression!");
34+
}
35+
decomp_buffer.resize(DECOMP_BUFFER_SIZE);
36+
}
37+
38+
zlibcontext::~zlibcontext() {
39+
inflateEnd(d_stream.get());
40+
}
41+
42+
exception_error_code zlibcontext::decompress(const std::string& buffer, std::string& decompressed) {
43+
decompressed.clear();
44+
/* This is safe; zlib requires us to cast away the const. The underlying buffer is unchanged. */
45+
d_stream->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(buffer.data()));
46+
d_stream->avail_in = static_cast<uInt>(buffer.size());
47+
do {
48+
d_stream->next_out = static_cast<Bytef*>(decomp_buffer.data());
49+
d_stream->avail_out = DECOMP_BUFFER_SIZE;
50+
int ret = inflate(d_stream.get(), Z_NO_FLUSH);
51+
size_t have = DECOMP_BUFFER_SIZE - d_stream->avail_out;
52+
switch (ret)
53+
{
54+
case Z_NEED_DICT:
55+
case Z_STREAM_ERROR:
56+
return err_compression_stream;
57+
case Z_DATA_ERROR:
58+
return err_compression_data;
59+
case Z_MEM_ERROR:
60+
return err_compression_memory;
61+
case Z_OK:
62+
decompressed.append(decomp_buffer.begin(), decomp_buffer.begin() + have);
63+
decompressed_total += have;
64+
break;
65+
default:
66+
/* Stub */
67+
break;
68+
}
69+
} while (d_stream->avail_out == 0);
70+
return err_no_code_specified;
71+
}
72+
73+
};

0 commit comments

Comments
 (0)