Line data Source code
1 : #include "datadog_agent_config.h"
2 :
3 : #include <algorithm>
4 : #include <chrono>
5 : #include <cstddef>
6 :
7 : #include "default_http_client.h"
8 : #include "environment.h"
9 : #include "parse_util.h"
10 : #include "threaded_event_scheduler.h"
11 :
12 : namespace datadog {
13 : namespace tracing {
14 :
15 614 : Expected<HTTPClient::URL> DatadogAgentConfig::parse(StringView input) {
16 614 : const StringView separator = "://";
17 614 : const auto after_scheme = std::search(input.begin(), input.end(),
18 : separator.begin(), separator.end());
19 614 : if (after_scheme == input.end()) {
20 1 : std::string message;
21 1 : message += "Datadog Agent URL is missing the \"://\" separator: \"";
22 1 : append(message, input);
23 1 : message += '\"';
24 1 : return Error{Error::URL_MISSING_SEPARATOR, std::move(message)};
25 1 : }
26 :
27 613 : const StringView scheme = range(input.begin(), after_scheme);
28 : const StringView supported[] = {"http", "https", "unix", "http+unix",
29 613 : "https+unix"};
30 : const auto found =
31 613 : std::find(std::begin(supported), std::end(supported), scheme);
32 613 : if (found == std::end(supported)) {
33 1 : std::string message;
34 1 : message += "Unsupported URI scheme \"";
35 1 : append(message, scheme);
36 1 : message += "\" in Datadog Agent URL \"";
37 1 : append(message, input);
38 1 : message += "\". The following are supported:";
39 6 : for (const auto& supported_scheme : supported) {
40 5 : message += ' ';
41 5 : append(message, supported_scheme);
42 : }
43 1 : return Error{Error::URL_UNSUPPORTED_SCHEME, std::move(message)};
44 1 : }
45 :
46 : const StringView authority_and_path =
47 612 : range(after_scheme + separator.size(), input.end());
48 : // If the scheme is for unix domain sockets, then there's no way to
49 : // distinguish the path-to-socket from the path-to-resource. Some
50 : // implementations require that the forward slashes in the path-to-socket
51 : // are URL-encoded. However, URLs that we will be parsing designate the
52 : // location of the Datadog Agent service, and so do not have a resource
53 : // location. Thus, if the scheme is for a unix domain socket, assume that
54 : // the entire part after the "://" is the path to the socket, and that
55 : // there is no resource path.
56 612 : if (scheme == "unix" || scheme == "http+unix" || scheme == "https+unix") {
57 4 : if (authority_and_path.empty() || authority_and_path[0] != '/') {
58 1 : std::string message;
59 : message +=
60 : "Unix domain socket paths for Datadog Agent must be absolute, i.e. "
61 : "must begin with a "
62 1 : "\"/\". The path \"";
63 1 : append(message, authority_and_path);
64 1 : message += "\" is not absolute. Error occurred for URL: \"";
65 1 : append(message, input);
66 1 : message += '\"';
67 2 : return Error{Error::URL_UNIX_DOMAIN_SOCKET_PATH_NOT_ABSOLUTE,
68 2 : std::move(message)};
69 1 : }
70 6 : return HTTPClient::URL{std::string(scheme), std::string(authority_and_path),
71 3 : ""};
72 : }
73 :
74 : // The scheme is either "http" or "https". This means that the part after
75 : // the "://" could be <resource>/<path>, e.g. "localhost:8080/api/v1".
76 : // Again, though, we're only parsing URLs that designate the location of
77 : // the Datadog Agent service, and so they will not have a resource
78 : // location. Still, let's parse it properly.
79 : const auto after_authority =
80 608 : std::find(authority_and_path.begin(), authority_and_path.end(), '/');
81 3040 : return HTTPClient::URL{
82 : std::string(scheme),
83 1216 : std::string(range(authority_and_path.begin(), after_authority)),
84 1824 : std::string(range(after_authority, authority_and_path.end()))};
85 : }
86 :
87 619 : Expected<FinalizedDatadogAgentConfig> finalize_config(
88 : const DatadogAgentConfig& config, const std::shared_ptr<Logger>& logger,
89 : const Clock& clock) {
90 619 : FinalizedDatadogAgentConfig result;
91 :
92 619 : result.clock = clock;
93 :
94 619 : if (!config.http_client) {
95 208 : result.http_client = default_http_client(logger, clock);
96 : // `default_http_client` might return a `Curl` instance depending on how
97 : // this library was built. If it returns `nullptr`, then there's no
98 : // built-in default, and so the user must provide a value.
99 208 : if (!result.http_client) {
100 0 : return Error{Error::DATADOG_AGENT_NULL_HTTP_CLIENT,
101 0 : "DatadogAgent: HTTP client cannot be null."};
102 : }
103 : } else {
104 411 : result.http_client = config.http_client;
105 : }
106 :
107 619 : if (!config.event_scheduler) {
108 208 : result.event_scheduler = std::make_shared<ThreadedEventScheduler>();
109 : } else {
110 411 : result.event_scheduler = config.event_scheduler;
111 : }
112 :
113 619 : if (config.flush_interval_milliseconds <= 0) {
114 4 : return Error{Error::DATADOG_AGENT_INVALID_FLUSH_INTERVAL,
115 : "DatadogAgent: Flush interval must be a positive number of "
116 2 : "milliseconds."};
117 : }
118 617 : result.flush_interval =
119 617 : std::chrono::milliseconds(config.flush_interval_milliseconds);
120 :
121 617 : int rc_poll_interval_seconds =
122 617 : config.remote_configuration_poll_interval_seconds;
123 :
124 617 : if (auto raw_rc_poll_interval_value =
125 617 : lookup(environment::DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS)) {
126 2 : auto res = parse_int(*raw_rc_poll_interval_value, 10);
127 2 : if (auto error = res.if_error()) {
128 2 : return error->with_prefix(
129 1 : "DatadogAgent: Remote Configuration poll interval error ");
130 : }
131 :
132 1 : rc_poll_interval_seconds = *res;
133 2 : }
134 :
135 616 : if (rc_poll_interval_seconds <= 0) {
136 4 : return Error{Error::DATADOG_AGENT_INVALID_REMOTE_CONFIG_POLL_INTERVAL,
137 : "DatadogAgent: Remote Configuration poll interval must be a "
138 2 : "positive number of seconds."};
139 : }
140 :
141 614 : result.remote_configuration_poll_interval =
142 614 : std::chrono::seconds(rc_poll_interval_seconds);
143 :
144 614 : auto env_host = lookup(environment::DD_AGENT_HOST);
145 614 : auto env_port = lookup(environment::DD_TRACE_AGENT_PORT);
146 :
147 614 : std::string configured_url = config.url;
148 614 : if (auto url_env = lookup(environment::DD_TRACE_AGENT_URL)) {
149 5 : assign(configured_url, *url_env);
150 609 : } else if (env_host || env_port) {
151 5 : configured_url.clear();
152 5 : configured_url += "http://";
153 5 : append(configured_url, env_host.value_or("localhost"));
154 5 : configured_url += ':';
155 5 : append(configured_url, env_port.value_or("8126"));
156 : }
157 :
158 614 : auto url = config.parse(configured_url);
159 614 : if (auto* error = url.if_error()) {
160 3 : return std::move(*error);
161 : }
162 611 : result.url = *url;
163 :
164 611 : return result;
165 619 : }
166 :
167 : } // namespace tracing
168 : } // namespace datadog
|