Skip to content

Commit 7f57e9d

Browse files
committed
Support DD_APM_TRACING_ENABLED=false
1 parent 967fc70 commit 7f57e9d

19 files changed

+597
-50
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
77
cmake_policy(SET CMP0068 NEW)
88
cmake_policy(SET CMP0135 NEW)
99

10-
set(NGINX_DATADOG_VERSION 1.6.1)
10+
set(NGINX_DATADOG_VERSION 1.7.0)
1111
project(ngx_http_datadog_module VERSION ${NGINX_DATADOG_VERSION})
1212

1313
option(NGINX_DATADOG_ASM_ENABLED "Build with libddwaf" ON)

src/common/headers.cpp

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
#include "headers.h"
22

3+
#include <memory>
4+
35
#include "string_util.h"
46

5-
namespace datadog::common {
7+
extern "C" {
8+
#include <ngx_hash.h>
9+
}
610

7-
ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key) {
8-
ngx_list_part_t *part = &headers.part;
9-
auto *h = static_cast<ngx_table_elt_t *>(part->elts);
11+
namespace {
12+
13+
template <bool delete_header>
14+
auto search_header_impl(ngx_list_t &headers, std::string_view key) {
15+
auto key_lc = std::unique_ptr<u_char[]>{new u_char[key.size()]};
16+
std::transform(key.begin(), key.end(), key_lc.get(),
17+
datadog::nginx::to_lower);
18+
ngx_uint_t key_hash = ngx_hash_key(key_lc.get(), key.size());
1019

20+
ngx_list_part_t *part = &headers.part;
21+
ngx_table_elt_t *h = static_cast<ngx_table_elt_t *>(part->elts);
1122
for (std::size_t i = 0;; i++) {
1223
if (i >= part->nelts) {
1324
if (part->next == nullptr) {
@@ -19,19 +30,42 @@ ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key) {
1930
i = 0;
2031
}
2132

22-
if (key.size() != h[i].key.len ||
23-
ngx_strncasecmp((u_char *)key.data(), h[i].key.data, key.size()) != 0) {
33+
if (h[i].hash != key_hash || key.size() != h[i].key.len ||
34+
memcmp(key_lc.get(), h[i].lowcase_key, key.size()) != 0) {
2435
continue;
2536
}
2637

27-
return &h[i];
38+
if constexpr (delete_header) {
39+
part->nelts--;
40+
if (i < part->nelts) {
41+
memmove(&h[i], &h[i + 1], (part->nelts - i) * sizeof(*h));
42+
}
43+
return true;
44+
} else {
45+
return &h[i];
46+
}
47+
}
48+
49+
if constexpr (delete_header) {
50+
return false;
51+
} else {
52+
return static_cast<ngx_table_elt_t *>(nullptr);
2853
}
54+
}
55+
} // namespace
56+
57+
namespace datadog::common {
2958

30-
return nullptr;
59+
ngx_table_elt_t *search_req_header(ngx_list_t &headers, std::string_view key) {
60+
return search_header_impl<false>(headers, key);
3161
}
3262

33-
bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
34-
std::string_view value) {
63+
bool delete_req_header(ngx_list_t &headers, std::string_view key) {
64+
return search_header_impl<true>(headers, key);
65+
}
66+
67+
bool add_req_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
68+
std::string_view value) {
3569
if (headers.last == nullptr) {
3670
// Certainly a bad request (4xx). No need to add HTTP headers.
3771
return false;
@@ -44,13 +78,6 @@ bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
4478

4579
const auto key_size = key.size();
4680

47-
// This trick tells ngx_http_header_module to reflect the header value
48-
// in the actual response. Otherwise the header will be ignored and client
49-
// will never see it. To date the value must be just non zero.
50-
// Source:
51-
// <https://web.archive.org/web/20240409072840/https://www.nginx.com/resources/wiki/start/topics/examples/headers_management/>
52-
h->hash = 1;
53-
5481
// HTTP proxy module expects the header to has a lowercased key value
5582
// Instead of allocating twice the same key, `h->key` and `h->lowcase_key`
5683
// use the same data.
@@ -61,6 +88,11 @@ bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
6188
}
6289
h->lowcase_key = h->key.data;
6390

91+
// In request headers, the hash should be calculated from the lowercase key.
92+
// See ngx_http_parse_header_line in ngx_http_parse.c
93+
// Response headers OTOH use either 1 or 0, with 0 meaning "skip this header".
94+
h->hash = ngx_hash_key(h->lowcase_key, key.size());
95+
6496
h->value = nginx::to_ngx_str(&pool, value);
6597
return true;
6698
}

src/common/headers.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ extern "C" {
99

1010
namespace datadog::common {
1111

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

28-
/// Adds a new HTTP header to an NGINX-style header list.
29+
/// Deletes a request header with the specified key from a NGINX-request header
30+
/// list.
31+
///
32+
/// @param headers
33+
/// A reference to an NGINX-style list (`ngx_list_t`) containing
34+
/// `ngx_table_elt_t` elements, typically representing HTTP headers.
35+
///
36+
/// @param key
37+
/// A string view representing the name of the header to delete.
38+
/// The comparison is case-insensitive.
39+
///
40+
/// @return
41+
/// `true` if a header with the given key was found and deleted;
42+
/// `false` if no header with the given key exists in the list.
43+
bool delete_req_header(ngx_list_t &headers, std::string_view key);
44+
45+
/// Adds a new HTTP request header to an NGINX-style header list.
2946
///
3047
/// @param pool
3148
/// A reference to the NGINX memory pool (`ngx_pool_t`) used for allocating
@@ -47,7 +64,7 @@ ngx_table_elt_t *search_header(ngx_list_t &headers, std::string_view key);
4764
/// @return
4865
/// `true` if the header was successfully added to the list;
4966
/// `false` if memory allocation failed or the list could not be updated.
50-
bool add_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
51-
std::string_view value);
67+
bool add_req_header(ngx_pool_t &pool, ngx_list_t &headers, std::string_view key,
68+
std::string_view value);
5269

5370
} // namespace datadog::common

src/datadog_conf.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ struct sampling_rule_t {
5757
};
5858

5959
struct datadog_main_conf_t {
60+
// DD_APM_TRACING_ENABLED
61+
// Whether we discard almost all traces not setting _dd.p.ts
62+
ngx_flag_t apm_tracing_enabled{NGX_CONF_UNSET};
63+
6064
std::unordered_map<std::string, ngx_http_complex_value_t *> tags;
6165
// `are_propagation_styles_locked` is whether the tracer's propagation styles
6266
// have been set, either by an explicit `datadog_propagation_styles`

src/datadog_context.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ DatadogContext::DatadogContext(ngx_http_request_t *request,
2222
datadog_loc_conf_t *loc_conf)
2323
#ifdef WITH_WAF
2424
: sec_ctx_{security::Context::maybe_create(
25-
*loc_conf, security::Library::max_saved_output_data())}
25+
security::Library::max_saved_output_data(),
26+
security::Library::apm_tracing_enabled())}
2627
#endif
2728
{
2829
if (loc_conf->enable_tracing) {

src/datadog_directive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern "C" {
77
#include <ngx_http.h>
88
}
99

10+
#include <array>
1011
#include <string_view>
1112

1213
#include "string_util.h"

src/ngx_header_writer.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ class NgxHeaderWriter : public datadog::tracing::DictWriter {
2424
: request_(request), pool_(request_->pool) {}
2525

2626
void set(std::string_view key, std::string_view value) override {
27+
if (value.empty()) {
28+
common::delete_req_header(request_->headers_in.headers, key);
29+
return;
30+
}
31+
2732
ngx_table_elt_t *h =
28-
common::search_header(request_->headers_in.headers, key);
33+
common::search_req_header(request_->headers_in.headers, key);
2934
if (h != nullptr) {
3035
h->value = to_ngx_str(pool_, value);
3136
} else {
32-
common::add_header(*pool_, request_->headers_in.headers, key, value);
37+
common::add_req_header(*pool_, request_->headers_in.headers, key, value);
3338
}
3439
}
3540
};

src/ngx_http_datadog_module.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
#include <cassert>
44
#include <cstdlib>
55
#include <exception>
6-
#include <iterator>
76
#include <memory>
8-
#include <new>
97
#include <string_view>
108
#include <utility>
119

10+
#include "datadog/injection_options.h"
1211
#include "datadog_conf.h"
1312
#include "datadog_conf_handler.h"
13+
#include "datadog_context.h"
1414
#include "datadog_directive.h"
1515
#include "datadog_handler.h"
1616
#include "datadog_variable.h"
@@ -28,6 +28,7 @@
2828
#include "rum/config.h"
2929
#endif
3030
#include "common/variable.h"
31+
#include "ngx_header_writer.h"
3132
#include "string_util.h"
3233
#include "tracing_library.h"
3334
#include "version.h"
@@ -278,6 +279,8 @@ static ngx_int_t datadog_master_process_post_config(
278279
return NGX_OK;
279280
}
280281

282+
static ngx_int_t on_precontent_phase(ngx_http_request_t *request) noexcept;
283+
281284
static ngx_int_t datadog_module_init(ngx_conf_t *cf) noexcept {
282285
ngx_http_next_header_filter = ngx_http_top_header_filter;
283286
ngx_http_top_header_filter = on_header_filter;
@@ -313,6 +316,11 @@ static ngx_int_t datadog_module_init(ngx_conf_t *cf) noexcept {
313316
}
314317
#endif
315318

319+
if (set_handler(cf->log, core_main_config, NGX_HTTP_PRECONTENT_PHASE,
320+
on_precontent_phase) != NGX_OK) {
321+
return NGX_ERROR;
322+
}
323+
316324
// Add default span tags.
317325
const auto tags = TracingLibrary::default_tags();
318326
if (!tags.empty()) {
@@ -493,3 +501,31 @@ static char *merge_datadog_loc_conf(ngx_conf_t *cf, void *parent,
493501

494502
return NGX_CONF_OK;
495503
}
504+
505+
static ngx_int_t on_precontent_phase(ngx_http_request_t *request) noexcept {
506+
auto *ctx = get_datadog_context(request);
507+
if (!ctx) {
508+
return NGX_DECLINED;
509+
}
510+
511+
// inject headers in the precontent phase into the request headers
512+
// These headers will be copied by ngx_http_proxy_create_request on the
513+
// content phase into the outgoing request headers (probably)
514+
RequestTracing &tracing = ctx->single_trace();
515+
dd::Span &span = tracing.active_span();
516+
span.set_tag("span.kind", "client");
517+
518+
datadog::tracing::InjectionOptions opts{};
519+
#ifdef WITH_WAF
520+
if (auto sec_ctx = ctx->get_security_context()) {
521+
if (sec_ctx->has_matches()) {
522+
opts.trace_source = {'0', '2'};
523+
}
524+
}
525+
#endif
526+
527+
NgxHeaderWriter writer(request);
528+
span.inject(writer, opts);
529+
530+
return NGX_DECLINED;
531+
}

src/request_tracing.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,6 @@ RequestTracing::RequestTracing(ngx_http_request_t *request,
240240
// We care about sampling rules for the request span only, because it's the
241241
// only span that could be the root span.
242242
set_sample_rate_tag(request_, loc_conf_, *request_span_);
243-
244-
// Inject the active span
245-
NgxHeaderWriter writer(request_);
246-
auto &span = active_span();
247-
span.set_tag("span.kind", "client");
248-
span.inject(writer);
249243
}
250244

251245
void RequestTracing::on_change_block(ngx_http_core_loc_conf_t *core_loc_conf,
@@ -275,12 +269,6 @@ void RequestTracing::on_change_block(ngx_http_core_loc_conf_t *core_loc_conf,
275269
// We care about sampling rules for the request span only, because it's the
276270
// only span that could be the root span.
277271
set_sample_rate_tag(request_, loc_conf_, *request_span_);
278-
279-
// Inject the active span
280-
NgxHeaderWriter writer(request_);
281-
auto &span = active_span();
282-
span.set_tag("span.kind", "client");
283-
span.inject(writer);
284272
}
285273

286274
dd::Span &RequestTracing::active_span() {

0 commit comments

Comments
 (0)