Skip to content

Commit 182f4bd

Browse files
authored
Merge pull request #171 from protegrity/av_snappy_09
Adding Snappy to API server flow
2 parents c55d22a + 2323177 commit 182f4bd

6 files changed

Lines changed: 300 additions & 24 deletions

File tree

CMakeLists.txt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ FetchContent_Declare(
8282
GIT_TAG master
8383
)
8484

85+
# Snappy is a fast compression/decompression library
86+
FetchContent_Declare(
87+
snappy
88+
GIT_REPOSITORY https://github.com/google/snappy.git
89+
GIT_TAG 1.2.2
90+
)
91+
8592
# Fetch dependencies
8693

8794
# Disable dependency tests for cppcodec
@@ -92,6 +99,19 @@ set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
9299
FetchContent_MakeAvailable(cppcodec)
93100
set(BUILD_TESTING ON CACHE BOOL "" FORCE)
94101

102+
# Disable Snappy tests to avoid conflicts with our GoogleTest
103+
# Snappy includes its own GoogleTest which conflicts with our FetchContent GoogleTest
104+
set(SNAPPY_BUILD_TESTS OFF CACHE BOOL "" FORCE)
105+
FetchContent_MakeAvailable(snappy)
106+
set(SNAPPY_BUILD_TESTS ON CACHE BOOL "" FORCE)
107+
108+
# Suppress warnings from Snappy's source code
109+
if(TARGET snappy)
110+
target_compile_options(snappy PRIVATE
111+
-Wno-sign-compare
112+
)
113+
endif()
114+
95115
FetchContent_MakeAvailable(crow httplib nlohmann_json cxxopts googletest jwt-cpp)
96116

97117
# Make GoogleTest helper macros available (gtest_discover_tests)
@@ -118,13 +138,15 @@ add_library(dbps_server_lib STATIC
118138
src/server/encryption_sequencer.cpp
119139
src/server/auth_utils.cpp
120140
src/server/decoding_utils.cpp
141+
src/server/compression_utils.cpp
121142
)
122-
target_link_libraries(dbps_server_lib PUBLIC dbps_common_lib)
143+
target_link_libraries(dbps_server_lib PUBLIC dbps_common_lib snappy)
123144
target_include_directories(dbps_server_lib PUBLIC
124145
src/server
125146
${CMAKE_BINARY_DIR}/_deps/cppcodec-src
126147
${CMAKE_BINARY_DIR}/_deps/jwt-cpp-src/include
127148
${CMAKE_BINARY_DIR}/_deps/nlohmann_json-src/include
149+
${CMAKE_BINARY_DIR}/_deps/snappy-src
128150
)
129151

130152
# Find and link OpenSSL (required by jwt-cpp)
@@ -200,9 +222,12 @@ target_link_libraries(dbpa_remote_testapp
200222
dbps_remote_lib
201223
dbps_client_lib
202224
dbps_common_lib
225+
dbps_server_lib
203226
)
204227
target_include_directories(dbpa_remote_testapp PRIVATE
205228
${CMAKE_BINARY_DIR}/_deps/cxxopts-src/include
229+
src/server
230+
${CMAKE_BINARY_DIR}/_deps/snappy-src
206231
)
207232

208233
# =============================================================================
@@ -244,6 +269,15 @@ if(BUILD_TESTS)
244269
)
245270
target_include_directories(decoding_utils_test PRIVATE src/server)
246271

272+
# Compression utils tests
273+
add_executable(compression_utils_test src/server/compression_utils_test.cpp)
274+
target_link_libraries(compression_utils_test
275+
dbps_server_lib
276+
dbps_common_lib
277+
gtest_main
278+
)
279+
target_include_directories(compression_utils_test PRIVATE src/server)
280+
247281
# Auth utils tests
248282
add_executable(auth_utils_test src/server/auth_utils_test.cpp)
249283
target_link_libraries(auth_utils_test
@@ -413,6 +447,7 @@ if(BUILD_TESTS)
413447
enum_utils_test
414448
encryption_sequencer_test
415449
decoding_utils_test
450+
compression_utils_test
416451
auth_utils_test
417452
dbpa_interface_test
418453
dbpa_utils_test
@@ -429,6 +464,7 @@ if(BUILD_TESTS)
429464
gtest_discover_tests(enum_utils_test)
430465
gtest_discover_tests(encryption_sequencer_test)
431466
gtest_discover_tests(decoding_utils_test)
467+
gtest_discover_tests(compression_utils_test)
432468
gtest_discover_tests(auth_utils_test)
433469
gtest_discover_tests(dbpa_interface_test)
434470
gtest_discover_tests(dbpa_utils_test)

src/scripts/dbpa_remote_testapp.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
#include "../common/dbpa_remote.h"
2929
#include "../client/httplib_client.h"
3030
#include "../common/enums.h"
31+
#include "../server/compression_utils.h"
3132
#include "tcb/span.hpp"
3233

3334
using namespace dbps::external;
3435
using namespace dbps::enum_utils;
36+
using namespace dbps::compression;
3537

3638
template <typename T>
3739
using span = tcb::span<T>;
@@ -143,7 +145,7 @@ class DBPARemoteTestApp {
143145
"demo_fixed_len_key_001", // column_key_id
144146
Type::FIXED_LEN_BYTE_ARRAY, // datatype
145147
8, // datatype_length (8 bytes per element)
146-
CompressionCodec::UNCOMPRESSED, // compression_type
148+
CompressionCodec::SNAPPY, // compression_type (input will be Snappy-compressed)
147149
VALID_ENCRYPTION_METADATA // column_encryption_metadata
148150
);
149151

@@ -464,11 +466,16 @@ class DBPARemoteTestApp {
464466
}
465467

466468
std::cout << "Test data: 3 fixed-length strings (8 bytes each)" << std::endl;
467-
std::cout << "Total size: " << fixed_length_data.size() << " bytes" << std::endl;
469+
std::cout << "Original size: " << fixed_length_data.size() << " bytes" << std::endl;
468470

471+
// Compress the test data using Snappy before sending to server
472+
std::vector<uint8_t> fixed_length_data_compressed = Compress(fixed_length_data, CompressionCodec::SNAPPY);
473+
std::cout << "Compressed size: " << fixed_length_data_compressed.size() << " bytes" << std::endl;
474+
469475
// Test encryption with FIXED_LEN_BYTE_ARRAY and datatype_length
476+
// The server will decompress the fixed_length_data_compressed, encrypt it, and compress the encrypted result
470477
std::map<std::string, std::string> fixed_len_encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}};
471-
auto encrypt_result = fixed_len_agent_->Encrypt(span<const uint8_t>(fixed_length_data), fixed_len_encoding_attributes);
478+
auto encrypt_result = fixed_len_agent_->Encrypt(span<const uint8_t>(fixed_length_data_compressed), fixed_len_encoding_attributes);
472479

473480
if (!encrypt_result || !encrypt_result->success()) {
474481
std::cout << "ERROR: FIXED_LEN_BYTE_ARRAY encryption failed" << std::endl;
@@ -493,13 +500,22 @@ class DBPARemoteTestApp {
493500

494501
std::cout << "OK: FIXED_LEN_BYTE_ARRAY decrypted successfully" << std::endl;
495502

503+
// The server returns compressed plaintext (same compression as input)
504+
// Decompress it before comparing with original data
505+
auto decrypted_compressed_span = decrypt_result->plaintext();
506+
std::vector<uint8_t> decrypted_compressed(decrypted_compressed_span.begin(), decrypted_compressed_span.end());
507+
std::vector<uint8_t> decrypted_data = Decompress(decrypted_compressed, CompressionCodec::SNAPPY);
508+
std::cout << "Decrypted compressed size: " << decrypted_compressed.size() << " bytes" << std::endl;
509+
std::cout << "Decrypted uncompressed size: " << decrypted_data.size() << " bytes" << std::endl;
510+
496511
// Verify data integrity
497-
auto decrypted_data = decrypt_result->plaintext();
498512
if (decrypted_data.size() == fixed_length_data.size() &&
499-
std::equal(decrypted_data.begin(), decrypted_data.end(), fixed_length_data.begin())) {
513+
std::equal(decrypted_data.begin(), decrypted_data.end(), fixed_length_data.begin(), fixed_length_data.end())) {
500514
std::cout << "OK: FIXED_LEN_BYTE_ARRAY data integrity verified" << std::endl;
501515
} else {
502516
std::cout << "ERROR: FIXED_LEN_BYTE_ARRAY data integrity check failed" << std::endl;
517+
std::cout << " Expected size: " << fixed_length_data.size() << " bytes" << std::endl;
518+
std::cout << " Got size: " << decrypted_data.size() << " bytes" << std::endl;
503519
return false;
504520
}
505521

src/server/compression_utils.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#include "compression_utils.h"
19+
#include <snappy.h>
20+
21+
using namespace dbps::external;
22+
using namespace dbps::enum_utils;
23+
24+
namespace dbps::compression {
25+
26+
std::vector<uint8_t> Compress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression) {
27+
if (compression == CompressionCodec::UNCOMPRESSED) {
28+
return bytes;
29+
}
30+
31+
if (compression == CompressionCodec::SNAPPY) {
32+
if (bytes.empty()) {
33+
return bytes;
34+
}
35+
// `compressed` is a std::string because Snappy's API requires it.
36+
// It is used as a binary buffer (not text), and immediately converted back to std::vector<uint8_t> to preserve binary semantics.
37+
std::string compressed;
38+
snappy::Compress(reinterpret_cast<const char*>(bytes.data()), bytes.size(), &compressed);
39+
return std::vector<uint8_t>(compressed.begin(), compressed.end());
40+
}
41+
42+
// Note for future implementations: If compression fails because of invalid or corrupt input,
43+
// then throw an InvalidInputException.
44+
throw DBPSUnsupportedException(
45+
"Unsupported compression codec: " + std::string(to_string(compression)));
46+
}
47+
48+
std::vector<uint8_t> Decompress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression) {
49+
if (compression == CompressionCodec::UNCOMPRESSED) {
50+
return bytes;
51+
}
52+
53+
if (compression == CompressionCodec::SNAPPY) {
54+
if (bytes.empty()) {
55+
return bytes;
56+
}
57+
// `decompressed` is a std::string because Snappy's API requires it.
58+
// It is used as a binary buffer (not text), and immediately converted back to std::vector<uint8_t> to preserve binary semantics.
59+
std::string decompressed;
60+
bool success = snappy::Uncompress(reinterpret_cast<const char*>(bytes.data()), bytes.size(), &decompressed);
61+
if (!success) {
62+
throw InvalidInputException("Failed to decompress data: invalid or corrupt Snappy-compressed input");
63+
}
64+
return std::vector<uint8_t>(decompressed.begin(), decompressed.end());
65+
}
66+
67+
// Note for future implementations: If decompression fails because of invalid or corrupt input,
68+
// then throw an InvalidInputException.
69+
throw DBPSUnsupportedException(
70+
"Unsupported compression codec: " + std::string(to_string(compression)));
71+
}
72+
73+
} // namespace dbps::compression

src/server/compression_utils.h

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
using namespace dbps::external;
2727
using namespace dbps::enum_utils;
2828

29+
namespace dbps::compression {
30+
2931
/**
3032
* Compress bytes using the compression codec.
3133
*
@@ -34,15 +36,7 @@ using namespace dbps::enum_utils;
3436
* @return Compressed bytes, or original bytes if UNCOMPRESSED
3537
* @throws DBPSUnsupportedException if the compression codec is not supported
3638
*/
37-
inline std::vector<uint8_t> Compress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression) {
38-
if (compression == CompressionCodec::UNCOMPRESSED) {
39-
return bytes;
40-
}
41-
// Note for future implementations: If compression fails because of invalid or corrupt input,
42-
// then throw an InvalidInputException.
43-
throw DBPSUnsupportedException(
44-
"Unsupported compression codec: " + std::string(to_string(compression)));
45-
}
39+
std::vector<uint8_t> Compress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression);
4640

4741
/**
4842
* Decompress bytes using the compression codec.
@@ -52,13 +46,6 @@ inline std::vector<uint8_t> Compress(const std::vector<uint8_t>& bytes, Compress
5246
* @return Decompressed bytes, or original bytes if UNCOMPRESSED
5347
* @throws DBPSUnsupportedException if the compression codec is not supported
5448
*/
55-
inline std::vector<uint8_t> Decompress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression) {
56-
if (compression == CompressionCodec::UNCOMPRESSED) {
57-
return bytes;
58-
}
59-
// Note for future implementations: If decompression fails because of invalid or corrupt input,
60-
// then throw an InvalidInputException.
61-
throw DBPSUnsupportedException(
62-
"Unsupported compression codec: " + std::string(to_string(compression)));
63-
}
49+
std::vector<uint8_t> Decompress(const std::vector<uint8_t>& bytes, CompressionCodec::type compression);
6450

51+
} // namespace dbps::compression

0 commit comments

Comments
 (0)