Skip to content

Support DD_APM_TRACING_ENABLED=false #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
cmake_policy(SET CMP0068 NEW)
cmake_policy(SET CMP0135 NEW)

set(NGINX_DATADOG_VERSION 1.6.1)
set(NGINX_DATADOG_VERSION 1.7.0)
project(ngx_http_datadog_module VERSION ${NGINX_DATADOG_VERSION})

option(NGINX_DATADOG_ASM_ENABLED "Build with libddwaf" ON)
Expand Down
2 changes: 1 addition & 1 deletion dd-trace-cpp
Submodule dd-trace-cpp updated 44 files
+2 −2 .github/workflows/codeql-analysis.yml
+10 −0 CMakePresets.json
+1 −1 examples/http-server/Dockerfile
+1 −0 examples/http-server/docker-compose.yaml
+2 −2 include/datadog/collector.h
+1 −0 include/datadog/config.h
+7 −0 include/datadog/datadog_agent_config.h
+3 −1 include/datadog/environment.h
+10 −1 include/datadog/injection_options.h
+1 −1 include/datadog/null_collector.h
+1 −1 include/datadog/sampling_mechanism.h
+5 −3 include/datadog/trace_segment.h
+7 −1 include/datadog/tracer.h
+12 −0 include/datadog/tracer_config.h
+4 −2 src/datadog/config_manager.cpp
+4 −1 src/datadog/config_manager.h
+23 −13 src/datadog/curl.cpp
+61 −11 src/datadog/datadog_agent.cpp
+6 −4 src/datadog/datadog_agent.h
+14 −0 src/datadog/datadog_agent_config.cpp
+1 −1 src/datadog/extraction_util.cpp
+128 −2 src/datadog/platform_util.cpp
+28 −0 src/datadog/platform_util.h
+2 −0 src/datadog/tags.cpp
+2 −0 src/datadog/tags.h
+100 −27 src/datadog/telemetry/telemetry_impl.cpp
+0 −2 src/datadog/telemetry/telemetry_impl.h
+36 −0 src/datadog/telemetry_metrics.cpp
+68 −5 src/datadog/telemetry_metrics.h
+93 −0 src/datadog/trace_sampler.cpp
+46 −0 src/datadog/trace_sampler.h
+78 −7 src/datadog/trace_segment.cpp
+49 −13 src/datadog/tracer.cpp
+13 −0 src/datadog/tracer_config.cpp
+0 −0 src/datadog/tracer_telemetry.cpp
+1 −0 test/CMakeLists.txt
+5 −5 test/mocks/collectors.h
+82 −67 test/telemetry/test_telemetry.cpp
+46 −16 test/test_curl.cpp
+59 −2 test/test_datadog_agent.cpp
+66 −0 test/test_platform_util.cpp
+144 −0 test/test_span.cpp
+134 −0 test/test_tracer.cpp
+32 −0 test/test_tracer_config.cpp
66 changes: 49 additions & 17 deletions src/common/headers.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
#include "headers.h"

#include <memory>

#include "string_util.h"

namespace datadog::common {
extern "C" {
#include <ngx_hash.h>
}

ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key) {
ngx_list_part_t *part = &headers.part;
auto *h = static_cast<ngx_table_elt_t *>(part->elts);
namespace {

template <bool delete_header>
auto search_header_impl(ngx_list_t &headers, std::string_view key) {
auto key_lc = std::unique_ptr<u_char[]>{new u_char[key.size()]};
std::transform(key.begin(), key.end(), key_lc.get(),
datadog::nginx::to_lower);
ngx_uint_t key_hash = ngx_hash_key(key_lc.get(), key.size());

ngx_list_part_t *part = &headers.part;
ngx_table_elt_t *h = static_cast<ngx_table_elt_t *>(part->elts);
for (std::size_t i = 0;; i++) {
if (i >= part->nelts) {
if (part->next == nullptr) {
Expand All @@ -19,19 +30,42 @@ ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key) {
i = 0;
}

if (key.size() != h[i].key.len ||
ngx_strncasecmp((u_char *)key.data(), h[i].key.data, key.size()) != 0) {
if (h[i].hash != key_hash || key.size() != h[i].key.len ||
memcmp(key_lc.get(), h[i].lowcase_key, key.size()) != 0) {
continue;
}

return &h[i];
if constexpr (delete_header) {
part->nelts--;
if (i < part->nelts) {
memmove(&h[i], &h[i + 1], (part->nelts - i) * sizeof(*h));
}
return true;
} else {
return &h[i];
}
}

if constexpr (delete_header) {
return false;
} else {
return static_cast<ngx_table_elt_t *>(nullptr);
}
}
} // namespace

namespace datadog::common {

return nullptr;
ngx_table_elt_t *search_req_header(ngx_list_t &headers, std::string_view key) {
return search_header_impl<false>(headers, key);
}

bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
std::string_view value) {
bool delete_req_header(ngx_list_t &headers, std::string_view key) {
return search_header_impl<true>(headers, key);
}

bool add_req_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
std::string_view value) {
if (headers.last == nullptr) {
// Certainly a bad request (4xx). No need to add HTTP headers.
return false;
Expand All @@ -44,13 +78,6 @@ bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,

const auto key_size = key.size();

// This trick tells ngx_http_header_module to reflect the header value
// in the actual response. Otherwise the header will be ignored and client
// will never see it. To date the value must be just non zero.
// Source:
// <https://web.archive.org/web/20240409072840/https://www.nginx.com/resources/wiki/start/topics/examples/headers_management/>
h->hash = 1;

// HTTP proxy module expects the header to has a lowercased key value
// Instead of allocating twice the same key, `h->key` and `h->lowcase_key`
// use the same data.
Expand All @@ -61,6 +88,11 @@ bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
}
h->lowcase_key = h->key.data;

// In request headers, the hash should be calculated from the lowercase key.
// See ngx_http_parse_header_line in ngx_http_parse.c
// Response headers OTOH use either 1 or 0, with 0 meaning "skip this header".
h->hash = ngx_hash_key(h->lowcase_key, key.size());

h->value = nginx::to_ngx_str(&pool, value);
return true;
}
Expand Down
27 changes: 22 additions & 5 deletions src/common/headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ extern "C" {

namespace datadog::common {

/// Searches through an NGINX header list to find a header with a matching key.
/// Searches through an NGINX request header list to find a header with a
/// matching key.
///
/// @param headers
/// A reference to an NGINX-style list (`ngx_list_t`) containing
Expand All @@ -23,9 +24,25 @@ namespace datadog::common {
/// A pointer to the matching `ngx_table_elt_t` header element if found,
/// or `nullptr` if no header with the given key exists in the list.
////
ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key);
ngx_table_elt_t *search_req_header(ngx_list_t &headers, std::string_view key);

/// Adds a new HTTP header to an NGINX-style header list.
/// Deletes a request header with the specified key from a NGINX-request header
/// list.
///
/// @param headers
/// A reference to an NGINX-style list (`ngx_list_t`) containing
/// `ngx_table_elt_t` elements, typically representing HTTP headers.
///
/// @param key
/// A string view representing the name of the header to delete.
/// The comparison is case-insensitive.
///
/// @return
/// `true` if a header with the given key was found and deleted;
/// `false` if no header with the given key exists in the list.
bool delete_req_header(ngx_list_t &headers, std::string_view key);

/// Adds a new HTTP request header to an NGINX-style header list.
///
/// @param pool
/// A reference to the NGINX memory pool (`ngx_pool_t`) used for allocating
Expand All @@ -47,7 +64,7 @@ ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key);
/// @return
/// `true` if the header was successfully added to the list;
/// `false` if memory allocation failed or the list could not be updated.
bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
std::string_view value);
bool add_req_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
std::string_view value);

} // namespace datadog::common
4 changes: 4 additions & 0 deletions src/datadog_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ struct sampling_rule_t {
};

struct datadog_main_conf_t {
// DD_APM_TRACING_ENABLED
// Whether we discard almost all traces not setting _dd.p.ts
ngx_flag_t apm_tracing_enabled{NGX_CONF_UNSET};

std::unordered_map<std::string, ngx_http_complex_value_t *> tags;
// `are_propagation_styles_locked` is whether the tracer's propagation styles
// have been set, either by an explicit `datadog_propagation_styles`
Expand Down
3 changes: 2 additions & 1 deletion src/datadog_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ DatadogContext::DatadogContext(ngx_http_request_t *request,
datadog_loc_conf_t *loc_conf)
#ifdef WITH_WAF
: sec_ctx_{security::Context::maybe_create(
*loc_conf, security::Library::max_saved_output_data())}
security::Library::max_saved_output_data(),
security::Library::apm_tracing_enabled())}
#endif
{
if (loc_conf->enable_tracing) {
Expand Down
1 change: 1 addition & 0 deletions src/datadog_directive.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern "C" {
#include <ngx_http.h>
}

#include <array>
#include <string_view>

#include "string_util.h"
Expand Down
9 changes: 7 additions & 2 deletions src/ngx_header_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ class NgxHeaderWriter : public datadog::tracing::DictWriter {
: request_(request), pool_(request_->pool) {}

void set(std::string_view key, std::string_view value) override {
if (value.empty()) {
common::delete_req_header(request_->headers_in.headers, key);
return;
}

ngx_table_elt_t *h =
common::search_header(request_->headers_in.headers, key);
common::search_req_header(request_->headers_in.headers, key);
if (h != nullptr) {
h->value = to_ngx_str(pool_, value);
} else {
common::add_header(*pool_, request_->headers_in.headers, key, value);
common::add_req_header(*pool_, request_->headers_in.headers, key, value);
}
}
};
Expand Down
40 changes: 38 additions & 2 deletions src/ngx_http_datadog_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
#include <cassert>
#include <cstdlib>
#include <exception>
#include <iterator>
#include <memory>
#include <new>
#include <string_view>
#include <utility>

#include "datadog/injection_options.h"
#include "datadog_conf.h"
#include "datadog_conf_handler.h"
#include "datadog_context.h"
#include "datadog_directive.h"
#include "datadog_handler.h"
#include "datadog_variable.h"
Expand All @@ -28,6 +28,7 @@
#include "rum/config.h"
#endif
#include "common/variable.h"
#include "ngx_header_writer.h"
#include "string_util.h"
#include "tracing_library.h"
#include "version.h"
Expand Down Expand Up @@ -278,6 +279,8 @@ static ngx_int_t datadog_master_process_post_config(
return NGX_OK;
}

static ngx_int_t on_precontent_phase(ngx_http_request_t *request) noexcept;

static ngx_int_t datadog_module_init(ngx_conf_t *cf) noexcept {
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = on_header_filter;
Expand Down Expand Up @@ -313,6 +316,11 @@ static ngx_int_t datadog_module_init(ngx_conf_t *cf) noexcept {
}
#endif

if (set_handler(cf->log, core_main_config, NGX_HTTP_PRECONTENT_PHASE,
on_precontent_phase) != NGX_OK) {
return NGX_ERROR;
}

// Add default span tags.
const auto tags = TracingLibrary::default_tags();
if (!tags.empty()) {
Expand Down Expand Up @@ -493,3 +501,31 @@ static char *merge_datadog_loc_conf(ngx_conf_t *cf, void *parent,

return NGX_CONF_OK;
}

static ngx_int_t on_precontent_phase(ngx_http_request_t *request) noexcept {
auto *ctx = get_datadog_context(request);
if (!ctx) {
return NGX_DECLINED;
}

// inject headers in the precontent phase into the request headers
// These headers will be copied by ngx_http_proxy_create_request on the
// content phase into the outgoing request headers (probably)
RequestTracing &tracing = ctx->single_trace();
dd::Span &span = tracing.active_span();
span.set_tag("span.kind", "client");

datadog::tracing::InjectionOptions opts{};
#ifdef WITH_WAF
if (auto sec_ctx = ctx->get_security_context()) {
if (sec_ctx->has_matches()) {
opts.trace_source = {'0', '2'};
}
}
#endif

NgxHeaderWriter writer(request);
span.inject(writer, opts);

return NGX_DECLINED;
}
12 changes: 0 additions & 12 deletions src/request_tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,6 @@ RequestTracing::RequestTracing(ngx_http_request_t *request,
// We care about sampling rules for the request span only, because it's the
// only span that could be the root span.
set_sample_rate_tag(request_, loc_conf_, *request_span_);

// Inject the active span
NgxHeaderWriter writer(request_);
auto &span = active_span();
span.set_tag("span.kind", "client");
span.inject(writer);
}

void RequestTracing::on_change_block(ngx_http_core_loc_conf_t *core_loc_conf,
Expand Down Expand Up @@ -275,12 +269,6 @@ void RequestTracing::on_change_block(ngx_http_core_loc_conf_t *core_loc_conf,
// We care about sampling rules for the request span only, because it's the
// only span that could be the root span.
set_sample_rate_tag(request_, loc_conf_, *request_span_);

// Inject the active span
NgxHeaderWriter writer(request_);
auto &span = active_span();
span.set_tag("span.kind", "client");
span.inject(writer);
}

dd::Span &RequestTracing::active_span() {
Expand Down
18 changes: 13 additions & 5 deletions src/security/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,11 @@ auto catch_exceptions(std::string_view name, const ngx_http_request_t &req,

namespace datadog::nginx::security {

Context::Context(std::shared_ptr<OwnedDdwafHandle> handle)
: stage_{new std::atomic<stage>{}}, waf_handle_{std::move(handle)} {
Context::Context(std::shared_ptr<OwnedDdwafHandle> handle,
bool apm_tracing_enabled)
: stage_{new std::atomic<stage>{}},
waf_handle_{std::move(handle)},
apm_tracing_enabled_{apm_tracing_enabled} {
if (!waf_handle_) {
return;
}
Expand All @@ -210,13 +213,14 @@ Context::Context(std::shared_ptr<OwnedDdwafHandle> handle)
}

std::unique_ptr<Context> Context::maybe_create(
datadog_loc_conf_t &loc_conf,
std::optional<std::size_t> max_saved_output_data) {
std::optional<std::size_t> max_saved_output_data,
bool apm_tracing_enabled) {
std::shared_ptr<OwnedDdwafHandle> handle = Library::get_handle();
if (!handle) {
return {};
}
auto res = std::unique_ptr<Context>{new Context{std::move(handle)}};
auto res = std::unique_ptr<Context>{
new Context{std::move(handle), apm_tracing_enabled}};
if (max_saved_output_data) {
res->max_saved_output_data_ = *max_saved_output_data;
}
Expand Down Expand Up @@ -1786,6 +1790,10 @@ void Context::report_matches(ngx_http_request_t &request, dd::Span &span) {

report_match(request, span.trace_segment(), span, results_);
results_.clear();

if (!apm_tracing_enabled_) {
span.set_tag("_dd.p.ts"sv, "02"sv);
}
}

void Context::report_client_ip(dd::Span &span) const {
Expand Down
Loading