diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f20a790f..4bd2c52fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ## Unreleased -**Features** +**Features**: - Add user feedback capability to the Native SDK ([#966](https://github.com/getsentry/sentry-native/pull/966)) +- Add optional Gzip transport compression via build option `SENTRY_TRANSPORT_COMPRESSION`. Requires system `zlib`. ([#954](https://github.com/getsentry/sentry-native/pull/954)) +- Add user feedback capability to the Native SDK ([#966](https://github.com/getsentry/sentry-native/pull/966)) **Internal**: @@ -14,6 +15,10 @@ - Add usage of the breadcrumb `data` property to the example. [#951](https://github.com/getsentry/sentry-native/pull/951) +**Thank you**: + +- [@Strive-Sun](https://github.com/Strive-Sun) + ## 0.7.0 **Breaking changes**: @@ -39,6 +44,7 @@ Features, fixes and improvements in this release have been contributed by: - [@compnerd](https://github.com/compnerd) - [@stima](https://github.com/stima) +- [@hyp](https://github.com/hyp) ## 0.6.7 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e8ffcdac..3996e83db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,8 @@ endif() option(SENTRY_PIC "Build sentry (and dependent) libraries as position independent libraries" ON) +option(SENTRY_TRANSPORT_COMPRESSION "Enable transport gzip compression" OFF) + option(SENTRY_BUILD_TESTS "Build sentry-native tests" "${SENTRY_MAIN_PROJECT}") option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}") @@ -278,17 +280,20 @@ if(SENTRY_TRANSPORT_CURL) find_package(CURL REQUIRED COMPONENTS AsynchDNS) endif() - if(TARGET CURL::libcurl) # Only available in cmake 3.12+ - target_link_libraries(sentry PRIVATE CURL::libcurl) - else() - # Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) - target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR}) - # The exported sentry target must not contain any path of the build machine, therefore use generator expressions - string(REPLACE ";" "$" GENEX_CURL_LIBRARIES "${CURL_LIBRARIES}") - string(REPLACE ";" "$" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}") - target_link_libraries(sentry PRIVATE $) - target_compile_definitions(sentry PRIVATE $) + target_link_libraries(sentry PRIVATE CURL::libcurl) +endif() + +if(SENTRY_TRANSPORT_COMPRESSION) + if(NOT ZLIB_FOUND) + find_package(ZLIB REQUIRED) endif() + + if(SENTRY_BACKEND_CRASHPAD) + set(CRASHPAD_ZLIB_SYSTEM ON CACHE BOOL "Force CRASHPAD_ZLIB_SYSTEM when enabling transport compression" FORCE) + endif() + + target_link_libraries(sentry PRIVATE ZLIB::ZLIB) + target_compile_definitions(sentry PRIVATE SENTRY_TRANSPORT_COMPRESSION) endif() set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden) @@ -413,9 +418,6 @@ if(SENTRY_WITH_LIBUNWINDSTACK) endif() if(SENTRY_BACKEND_CRASHPAD) - # FIXME: required for cmake 3.12 and lower: - # - NEW behavior lets normal variable override option - cmake_policy(SET CMP0077 NEW) if(SENTRY_BUILD_SHARED_LIBS) set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE) else() @@ -593,4 +595,4 @@ endif() if(SENTRY_BUILD_SHARED_LIBS) target_link_libraries(sentry PRIVATE "$<$,$>:-Wl,--build-id=sha1,--version-script=${PROJECT_SOURCE_DIR}/src/exports.map>") -endif() \ No newline at end of file +endif() diff --git a/external/crashpad b/external/crashpad index 6c9b05f36..6bde832e9 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 6c9b05f368edb80ac113a54b49007c053eee1c97 +Subproject commit 6bde832e99297b2db4c98676067368b6a8428ecc diff --git a/src/sentry_transport.c b/src/sentry_transport.c index ea62c5620..06ba3524b 100644 --- a/src/sentry_transport.c +++ b/src/sentry_transport.c @@ -5,9 +5,19 @@ #include "sentry_ratelimiter.h" #include "sentry_string.h" +#ifdef SENTRY_TRANSPORT_COMPRESSION +# include "zlib.h" +#endif + #define ENVELOPE_MIME "application/x-sentry-envelope" +#ifdef SENTRY_TRANSPORT_COMPRESSION +// The headers we use are: `x-sentry-auth`, `content-type`, `content-encoding`, +// `content-length` +# define MAX_HTTP_HEADERS 4 +#else // The headers we use are: `x-sentry-auth`, `content-type`, `content-length` -#define MAX_HTTP_HEADERS 3 +# define MAX_HTTP_HEADERS 3 +#endif typedef struct sentry_transport_s { void (*send_envelope_func)(sentry_envelope_t *envelope, void *state); @@ -148,6 +158,56 @@ sentry_transport_free(sentry_transport_t *transport) sentry_free(transport); } +#ifdef SENTRY_TRANSPORT_COMPRESSION +static bool +gzipped_with_compression(const char *body, const size_t body_len, + char **compressed_body, size_t *compressed_body_len) +{ + if (!body || body_len == 0) { + return false; + } + + z_stream stream; + memset(&stream, 0, sizeof(stream)); + stream.next_in = body; + stream.avail_in = body_len; + + int err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + MAX_WBITS + 16, 9, Z_DEFAULT_STRATEGY); + if (err != Z_OK) { + SENTRY_TRACEF("deflateInit2 failed: %d", err); + return false; + } + + size_t len = compressBound(body_len); + char *buffer = sentry_malloc(len); + if (!buffer) { + deflateEnd(&stream); + return false; + } + + while (err == Z_OK) { + stream.next_out = buffer + stream.total_out; + stream.avail_out = len - stream.total_out; + err = deflate(&stream, Z_FINISH); + } + + if (err != Z_STREAM_END) { + SENTRY_TRACEF("deflate failed: %d", err); + sentry_free(buffer); + buffer = NULL; + deflateEnd(&stream); + return false; + } + + *compressed_body_len = stream.total_out; + *compressed_body = buffer; + + deflateEnd(&stream); + return true; +} +#endif + sentry_prepared_http_request_t * sentry__prepare_http_request(sentry_envelope_t *envelope, const sentry_dsn_t *dsn, const sentry_rate_limiter_t *rl, @@ -165,6 +225,23 @@ sentry__prepare_http_request(sentry_envelope_t *envelope, return NULL; } + bool compressed = false; +#ifdef SENTRY_TRANSPORT_COMPRESSION + char *compressed_body = NULL; + size_t compressed_body_len = 0; + compressed = gzipped_with_compression( + body, body_len, &compressed_body, &compressed_body_len); + if (compressed) { + if (body_owned) { + sentry_free(body); + body_owned = false; + } + body = compressed_body; + body_len = compressed_body_len; + body_owned = true; + } +#endif + sentry_prepared_http_request_t *req = SENTRY_MAKE(sentry_prepared_http_request_t); if (!req) { @@ -196,6 +273,12 @@ sentry__prepare_http_request(sentry_envelope_t *envelope, h->key = "content-type"; h->value = sentry__string_clone(ENVELOPE_MIME); + if (compressed) { + h = &req->headers[req->headers_len++]; + h->key = "content-encoding"; + h->value = sentry__string_clone("gzip"); + } + h = &req->headers[req->headers_len++]; h->key = "content-length"; h->value = sentry__int64_to_string((int64_t)body_len);