datadog 2.21.0 → 2.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (623) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +574 -1
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  4. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  5. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +66 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +3 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +301 -82
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -4
  9. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +63 -32
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +7 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +728 -436
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +19 -6
  15. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +65 -1
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +40 -1
  18. data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
  19. data/ext/datadog_profiling_native_extension/extconf.rb +41 -37
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
  21. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
  22. data/ext/datadog_profiling_native_extension/heap_recorder.c +252 -103
  23. data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
  24. data/ext/datadog_profiling_native_extension/http_transport.c +66 -68
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
  27. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  28. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +40 -43
  30. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -0
  31. data/ext/datadog_profiling_native_extension/profiling.c +65 -19
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.c +63 -46
  33. data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
  34. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +32 -10
  35. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +2 -1
  36. data/ext/datadog_profiling_native_extension/stack_recorder.c +110 -85
  37. data/ext/datadog_profiling_native_extension/stack_recorder.h +3 -1
  38. data/ext/datadog_profiling_native_extension/time_helpers.h +13 -7
  39. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
  40. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +9 -4
  41. data/ext/libdatadog_api/crashtracker.c +16 -15
  42. data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
  43. data/ext/libdatadog_api/datadog_ruby_common.c +65 -1
  44. data/ext/libdatadog_api/datadog_ruby_common.h +40 -1
  45. data/ext/libdatadog_api/ddsketch.c +102 -0
  46. data/ext/libdatadog_api/di.c +203 -0
  47. data/ext/libdatadog_api/extconf.rb +18 -24
  48. data/ext/libdatadog_api/feature_flags.c +554 -0
  49. data/ext/libdatadog_api/feature_flags.h +5 -0
  50. data/ext/libdatadog_api/helpers.h +27 -0
  51. data/ext/libdatadog_api/init.c +13 -1
  52. data/ext/libdatadog_api/library_config.c +34 -25
  53. data/ext/libdatadog_api/process_discovery.c +19 -13
  54. data/ext/libdatadog_extconf_helpers.rb +94 -11
  55. data/lib/datadog/ai_guard/api_client.rb +84 -0
  56. data/lib/datadog/ai_guard/autoload.rb +10 -0
  57. data/lib/datadog/ai_guard/component.rb +44 -0
  58. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  59. data/lib/datadog/ai_guard/configuration.rb +115 -0
  60. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  61. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  62. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  63. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  64. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +97 -0
  65. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  66. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  67. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +80 -0
  68. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  69. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  70. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  71. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  72. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  73. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +36 -0
  74. data/lib/datadog/ai_guard/evaluation/request.rb +86 -0
  75. data/lib/datadog/ai_guard/evaluation/result.rb +45 -0
  76. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  77. data/lib/datadog/ai_guard/evaluation.rb +108 -0
  78. data/lib/datadog/ai_guard/ext.rb +29 -0
  79. data/lib/datadog/ai_guard.rb +181 -0
  80. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  81. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +66 -0
  82. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +36 -0
  83. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  84. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  85. data/lib/datadog/appsec/api_security/route_extractor.rb +38 -9
  86. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  87. data/lib/datadog/appsec/assets/blocked.html +10 -1
  88. data/lib/datadog/appsec/assets/blocked.json +1 -1
  89. data/lib/datadog/appsec/assets/blocked.text +3 -1
  90. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  91. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  92. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  93. data/lib/datadog/appsec/assets.rb +1 -1
  94. data/lib/datadog/appsec/autoload.rb +1 -1
  95. data/lib/datadog/appsec/component.rb +16 -9
  96. data/lib/datadog/appsec/compressed_json.rb +3 -3
  97. data/lib/datadog/appsec/configuration.rb +427 -1
  98. data/lib/datadog/appsec/context.rb +51 -13
  99. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  100. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  101. data/lib/datadog/appsec/contrib/active_record/patcher.rb +4 -1
  102. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  103. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  104. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  105. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +144 -0
  106. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  107. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  108. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  109. data/lib/datadog/appsec/contrib/excon/patcher.rb +3 -1
  110. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +152 -12
  111. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  112. data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
  113. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +138 -12
  114. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +74 -24
  115. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +4 -2
  116. data/lib/datadog/appsec/contrib/graphql/integration.rb +2 -1
  117. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  118. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  119. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +52 -13
  120. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +40 -19
  121. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  122. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  123. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +48 -33
  124. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  125. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +40 -1
  126. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +26 -5
  127. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  128. data/lib/datadog/appsec/contrib/rails/patcher.rb +38 -0
  129. data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -0
  130. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  131. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +3 -1
  132. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +167 -11
  133. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +43 -13
  134. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  135. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +4 -4
  136. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +1 -1
  137. data/lib/datadog/appsec/counter_sampler.rb +25 -0
  138. data/lib/datadog/appsec/default_header_tags.rb +52 -0
  139. data/lib/datadog/appsec/event.rb +13 -31
  140. data/lib/datadog/appsec/ext.rb +3 -0
  141. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
  142. data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
  143. data/lib/datadog/appsec/metrics/collector.rb +43 -5
  144. data/lib/datadog/appsec/metrics/exporter.rb +16 -3
  145. data/lib/datadog/appsec/metrics/telemetry.rb +20 -3
  146. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +20 -1
  147. data/lib/datadog/appsec/metrics.rb +5 -5
  148. data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -6
  149. data/lib/datadog/appsec/remote.rb +44 -29
  150. data/lib/datadog/appsec/response.rb +18 -4
  151. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  152. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  153. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  154. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  155. data/lib/datadog/appsec/security_engine/engine.rb +26 -5
  156. data/lib/datadog/appsec/security_engine/result.rb +29 -9
  157. data/lib/datadog/appsec/security_engine/runner.rb +20 -10
  158. data/lib/datadog/appsec/security_event.rb +5 -7
  159. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  160. data/lib/datadog/appsec/utils/http/body.rb +38 -0
  161. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  162. data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
  163. data/lib/datadog/appsec/utils/http/media_type.rb +32 -26
  164. data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
  165. data/lib/datadog/appsec.rb +6 -3
  166. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  167. data/lib/datadog/core/configuration/base.rb +17 -5
  168. data/lib/datadog/core/configuration/components.rb +185 -17
  169. data/lib/datadog/core/configuration/components_state.rb +6 -1
  170. data/lib/datadog/core/configuration/config_helper.rb +11 -2
  171. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  172. data/lib/datadog/core/configuration/option.rb +32 -6
  173. data/lib/datadog/core/configuration/option_definition.rb +42 -14
  174. data/lib/datadog/core/configuration/options.rb +49 -12
  175. data/lib/datadog/core/configuration/settings.rb +115 -16
  176. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  177. data/lib/datadog/core/configuration/supported_configurations.rb +379 -300
  178. data/lib/datadog/core/configuration.rb +3 -3
  179. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  180. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  181. data/lib/datadog/core/crashtracking/component.rb +73 -21
  182. data/lib/datadog/core/crashtracking/tag_builder.rb +6 -0
  183. data/lib/datadog/core/ddsketch.rb +19 -0
  184. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  185. data/lib/datadog/core/environment/agent_info.rb +65 -1
  186. data/lib/datadog/core/environment/cgroup.rb +52 -25
  187. data/lib/datadog/core/environment/container.rb +140 -46
  188. data/lib/datadog/core/environment/ext.rb +13 -0
  189. data/lib/datadog/core/environment/identity.rb +40 -4
  190. data/lib/datadog/core/environment/process.rb +122 -0
  191. data/lib/datadog/core/environment/socket.rb +13 -0
  192. data/lib/datadog/core/environment/yjit.rb +2 -1
  193. data/lib/datadog/core/error.rb +6 -6
  194. data/lib/datadog/core/evp.rb +11 -0
  195. data/lib/datadog/core/feature_flags.rb +61 -0
  196. data/lib/datadog/core/knuth_sampler.rb +59 -0
  197. data/lib/datadog/core/logger.rb +1 -1
  198. data/lib/datadog/core/metrics/client.rb +6 -6
  199. data/lib/datadog/core/metrics/logging.rb +1 -1
  200. data/lib/datadog/core/pin.rb +8 -8
  201. data/lib/datadog/core/process_discovery.rb +21 -18
  202. data/lib/datadog/core/rate_limiter.rb +11 -1
  203. data/lib/datadog/core/remote/client/capabilities.rb +49 -6
  204. data/lib/datadog/core/remote/client.rb +15 -7
  205. data/lib/datadog/core/remote/component.rb +64 -36
  206. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  207. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  208. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  209. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  210. data/lib/datadog/core/remote/transport/config.rb +9 -36
  211. data/lib/datadog/core/remote/transport/http/config.rb +15 -55
  212. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  213. data/lib/datadog/core/remote/transport/http.rb +15 -24
  214. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  215. data/lib/datadog/core/remote/worker.rb +25 -37
  216. data/lib/datadog/core/runtime/metrics.rb +11 -2
  217. data/lib/datadog/core/semaphore.rb +1 -4
  218. data/lib/datadog/core/tag_builder.rb +0 -4
  219. data/lib/datadog/core/tag_normalizer.rb +84 -0
  220. data/lib/datadog/core/telemetry/component.rb +59 -15
  221. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  222. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  223. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  224. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  225. data/lib/datadog/core/telemetry/event/app_started.rb +177 -171
  226. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  227. data/lib/datadog/core/telemetry/event.rb +2 -7
  228. data/lib/datadog/core/telemetry/ext.rb +1 -0
  229. data/lib/datadog/core/telemetry/logger.rb +4 -2
  230. data/lib/datadog/core/telemetry/logging.rb +17 -5
  231. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  232. data/lib/datadog/core/telemetry/request.rb +17 -5
  233. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +8 -34
  234. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  235. data/lib/datadog/core/telemetry/transport/telemetry.rb +8 -17
  236. data/lib/datadog/core/telemetry/worker.rb +108 -32
  237. data/lib/datadog/core/transport/ext.rb +3 -0
  238. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  239. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  240. data/lib/datadog/core/transport/http/builder.rb +10 -6
  241. data/lib/datadog/core/transport/http/client.rb +80 -0
  242. data/lib/datadog/core/transport/http/response.rb +4 -0
  243. data/lib/datadog/core/transport/http.rb +24 -19
  244. data/lib/datadog/core/transport/parcel.rb +61 -9
  245. data/lib/datadog/core/transport/response.rb +16 -2
  246. data/lib/datadog/core/transport/transport.rb +90 -0
  247. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +2 -2
  248. data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +4 -3
  249. data/lib/datadog/core/utils/enumerable_compat.rb +29 -0
  250. data/lib/datadog/core/utils/fnv.rb +26 -0
  251. data/lib/datadog/core/utils/forking.rb +3 -1
  252. data/lib/datadog/core/utils/hash.rb +0 -23
  253. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  254. data/lib/datadog/core/utils/network.rb +22 -1
  255. data/lib/datadog/core/utils/only_once.rb +1 -1
  256. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  257. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  258. data/lib/datadog/core/utils/sequence.rb +2 -0
  259. data/lib/datadog/core/utils/spawn_monkey_patch.rb +68 -0
  260. data/lib/datadog/core/utils/time.rb +6 -2
  261. data/lib/datadog/core/utils.rb +3 -1
  262. data/lib/datadog/core/workers/async.rb +11 -2
  263. data/lib/datadog/core/workers/interval_loop.rb +51 -3
  264. data/lib/datadog/core/workers/polling.rb +2 -0
  265. data/lib/datadog/core/workers/queue.rb +96 -1
  266. data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
  267. data/lib/datadog/core.rb +10 -3
  268. data/lib/datadog/data_streams/configuration.rb +50 -0
  269. data/lib/datadog/data_streams/ext.rb +11 -0
  270. data/lib/datadog/data_streams/extensions.rb +16 -0
  271. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  272. data/lib/datadog/data_streams/processor.rb +511 -0
  273. data/lib/datadog/data_streams/transport/http/stats.rb +58 -0
  274. data/lib/datadog/data_streams/transport/http.rb +36 -0
  275. data/lib/datadog/data_streams/transport/stats.rb +39 -0
  276. data/lib/datadog/data_streams.rb +100 -0
  277. data/lib/datadog/di/base.rb +15 -7
  278. data/lib/datadog/di/boot.rb +7 -2
  279. data/lib/datadog/di/capture_expression.rb +21 -0
  280. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  281. data/lib/datadog/di/capture_limits.rb +41 -0
  282. data/lib/datadog/di/code_tracker.rb +217 -15
  283. data/lib/datadog/di/component.rb +139 -51
  284. data/lib/datadog/di/configuration.rb +241 -2
  285. data/lib/datadog/di/context.rb +82 -0
  286. data/lib/datadog/di/contrib/active_record.rb +34 -5
  287. data/lib/datadog/di/el/compiler.rb +168 -0
  288. data/lib/datadog/di/el/evaluator.rb +159 -0
  289. data/lib/datadog/di/el/expression.rb +42 -0
  290. data/lib/datadog/di/el.rb +5 -0
  291. data/lib/datadog/di/error.rb +44 -0
  292. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  293. data/lib/datadog/di/instrumenter.rb +623 -147
  294. data/lib/datadog/di/logger.rb +2 -2
  295. data/lib/datadog/di/probe.rb +122 -14
  296. data/lib/datadog/di/probe_builder.rb +124 -3
  297. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  298. data/lib/datadog/di/probe_file_loader.rb +12 -7
  299. data/lib/datadog/di/probe_manager.rb +195 -100
  300. data/lib/datadog/di/probe_notification_builder.rb +321 -90
  301. data/lib/datadog/di/probe_notifier_worker.rb +112 -36
  302. data/lib/datadog/di/probe_repository.rb +198 -0
  303. data/lib/datadog/di/proc_responder.rb +36 -0
  304. data/lib/datadog/di/redactor.rb +24 -2
  305. data/lib/datadog/di/remote.rb +258 -93
  306. data/lib/datadog/di/serializer.rb +310 -29
  307. data/lib/datadog/di/transport/diagnostics.rb +12 -42
  308. data/lib/datadog/di/transport/http/diagnostics.rb +4 -34
  309. data/lib/datadog/di/transport/http/input.rb +2 -34
  310. data/lib/datadog/di/transport/http.rb +43 -19
  311. data/lib/datadog/di/transport/input.rb +112 -38
  312. data/lib/datadog/di/utils.rb +42 -14
  313. data/lib/datadog/di.rb +204 -4
  314. data/lib/datadog/error_tracking/collector.rb +2 -1
  315. data/lib/datadog/error_tracking/component.rb +2 -2
  316. data/lib/datadog/error_tracking/configuration.rb +55 -2
  317. data/lib/datadog/error_tracking/filters.rb +2 -2
  318. data/lib/datadog/kit/appsec/events/v2.rb +60 -3
  319. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  320. data/lib/datadog/kit/tracing/method_tracer.rb +135 -0
  321. data/lib/datadog/open_feature/component.rb +100 -0
  322. data/lib/datadog/open_feature/configuration.rb +37 -0
  323. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  324. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  325. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  326. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  327. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  328. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  329. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  330. data/lib/datadog/open_feature/ext.rb +16 -0
  331. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  332. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  333. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
  334. data/lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb +49 -0
  335. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  336. data/lib/datadog/open_feature/native_evaluator.rb +60 -0
  337. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  338. data/lib/datadog/open_feature/provider.rb +191 -0
  339. data/lib/datadog/open_feature/remote.rb +67 -0
  340. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  341. data/lib/datadog/open_feature/transport.rb +121 -0
  342. data/lib/datadog/open_feature.rb +19 -0
  343. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  344. data/lib/datadog/opentelemetry/configuration/settings.rb +226 -0
  345. data/lib/datadog/opentelemetry/ext.rb +9 -0
  346. data/lib/datadog/opentelemetry/logs.rb +98 -0
  347. data/lib/datadog/opentelemetry/metrics.rb +90 -0
  348. data/lib/datadog/opentelemetry/sdk/configurator.rb +65 -1
  349. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  350. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +30 -0
  351. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +28 -0
  352. data/lib/datadog/opentelemetry/sdk/propagator.rb +9 -3
  353. data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
  354. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  355. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  356. data/lib/datadog/opentelemetry.rb +4 -0
  357. data/lib/datadog/profiling/collectors/code_provenance.rb +71 -12
  358. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +52 -4
  359. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +13 -5
  360. data/lib/datadog/profiling/collectors/info.rb +18 -4
  361. data/lib/datadog/profiling/collectors/thread_context.rb +10 -5
  362. data/lib/datadog/profiling/component.rb +82 -40
  363. data/lib/datadog/profiling/exporter.rb +41 -12
  364. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +24 -5
  365. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  366. data/lib/datadog/profiling/ext.rb +0 -2
  367. data/lib/datadog/profiling/flush.rb +23 -11
  368. data/lib/datadog/profiling/http_transport.rb +21 -8
  369. data/lib/datadog/profiling/load_native_extension.rb +2 -2
  370. data/lib/datadog/profiling/profiler.rb +31 -14
  371. data/lib/datadog/profiling/scheduler.rb +10 -9
  372. data/lib/datadog/profiling/stack_recorder.rb +0 -8
  373. data/lib/datadog/profiling/tag_builder.rb +37 -3
  374. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  375. data/lib/datadog/profiling/tasks/help.rb +1 -0
  376. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  377. data/lib/datadog/profiling.rb +1 -3
  378. data/lib/datadog/ruby_version.rb +25 -0
  379. data/lib/datadog/single_step_instrument.rb +2 -2
  380. data/lib/datadog/symbol_database/component.rb +734 -0
  381. data/lib/datadog/symbol_database/configuration.rb +73 -0
  382. data/lib/datadog/symbol_database/extensions.rb +19 -0
  383. data/lib/datadog/symbol_database/extractor.rb +1110 -0
  384. data/lib/datadog/symbol_database/file_hash.rb +48 -0
  385. data/lib/datadog/symbol_database/logger.rb +43 -0
  386. data/lib/datadog/symbol_database/remote.rb +180 -0
  387. data/lib/datadog/symbol_database/scope.rb +102 -0
  388. data/lib/datadog/symbol_database/scope_batcher.rb +292 -0
  389. data/lib/datadog/symbol_database/service_version.rb +66 -0
  390. data/lib/datadog/symbol_database/symbol.rb +69 -0
  391. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  392. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  393. data/lib/datadog/symbol_database/transport.rb +54 -0
  394. data/lib/datadog/symbol_database/uploader.rb +230 -0
  395. data/lib/datadog/symbol_database.rb +71 -0
  396. data/lib/datadog/tracing/buffer.rb +3 -3
  397. data/lib/datadog/tracing/component.rb +17 -17
  398. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  399. data/lib/datadog/tracing/configuration/ext.rb +22 -0
  400. data/lib/datadog/tracing/configuration/settings.rb +96 -4
  401. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
  402. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
  403. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
  404. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
  405. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
  406. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +10 -7
  407. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +28 -0
  408. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  409. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  410. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
  411. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +5 -2
  412. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  413. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +5 -2
  414. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +5 -2
  415. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +5 -2
  416. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +5 -2
  417. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +5 -2
  418. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +5 -2
  419. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  420. data/lib/datadog/tracing/contrib/active_job/patcher.rb +17 -1
  421. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  422. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +3 -4
  423. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  424. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  425. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -5
  426. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  427. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +2 -5
  428. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
  429. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  430. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +6 -8
  431. data/lib/datadog/tracing/contrib/component.rb +3 -3
  432. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  433. data/lib/datadog/tracing/contrib/configuration/resolver.rb +14 -4
  434. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
  435. data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
  436. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  437. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
  438. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  439. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +2 -6
  440. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +2 -2
  441. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
  442. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -6
  443. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  444. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -8
  445. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +16 -5
  446. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  447. data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -7
  448. data/lib/datadog/tracing/contrib/ext.rb +3 -1
  449. data/lib/datadog/tracing/contrib/extensions.rb +19 -2
  450. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +16 -9
  451. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  452. data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -7
  453. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  454. data/lib/datadog/tracing/contrib/grape/endpoint.rb +10 -7
  455. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
  456. data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
  457. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  458. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  459. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +75 -44
  460. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
  461. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +3 -7
  462. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +3 -7
  463. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
  464. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  465. data/lib/datadog/tracing/contrib/grpc.rb +1 -0
  466. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
  467. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -0
  468. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -0
  469. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +16 -5
  470. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
  471. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  472. data/lib/datadog/tracing/contrib/http/instrumentation.rb +4 -8
  473. data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
  474. data/lib/datadog/tracing/contrib/http.rb +1 -0
  475. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +16 -5
  476. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  477. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +7 -7
  478. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +16 -5
  479. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  480. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -8
  481. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -0
  482. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  483. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  484. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  485. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
  486. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  487. data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
  488. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  489. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  490. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  491. data/lib/datadog/tracing/contrib/karafka.rb +1 -0
  492. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +5 -5
  493. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +5 -8
  494. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
  495. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +3 -6
  496. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +2 -6
  497. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +2 -2
  498. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
  499. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +15 -18
  500. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +6 -8
  501. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +13 -0
  502. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +6 -2
  503. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +24 -0
  504. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
  505. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  506. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
  507. data/lib/datadog/tracing/contrib/racecar/event.rb +1 -5
  508. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +11 -1
  509. data/lib/datadog/tracing/contrib/rack/ext.rb +28 -0
  510. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +23 -0
  511. data/lib/datadog/tracing/contrib/rack/middlewares.rb +62 -27
  512. data/lib/datadog/tracing/contrib/rack/patcher.rb +2 -2
  513. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  514. data/lib/datadog/tracing/contrib/rack/route_inference.rb +67 -0
  515. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +119 -1
  516. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
  517. data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
  518. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  519. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  520. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  521. data/lib/datadog/tracing/contrib/rails/runner.rb +3 -1
  522. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +6 -4
  523. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  524. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -6
  525. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +2 -0
  526. data/lib/datadog/tracing/contrib/registerable.rb +11 -0
  527. data/lib/datadog/tracing/contrib/resque/resque_job.rb +1 -0
  528. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
  529. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  530. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -6
  531. data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
  532. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +7 -2
  533. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -0
  534. data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -0
  535. data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -7
  536. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
  537. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
  538. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
  539. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
  540. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  541. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  542. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  543. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  544. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  545. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  546. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +3 -2
  547. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  548. data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
  549. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
  550. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  551. data/lib/datadog/tracing/contrib/sinatra/framework.rb +1 -1
  552. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
  553. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +4 -1
  554. data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
  555. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
  556. data/lib/datadog/tracing/contrib/status_range_matcher.rb +13 -1
  557. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  558. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
  559. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
  560. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -6
  561. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +4 -2
  562. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +31 -0
  563. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +50 -0
  564. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +18 -0
  565. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  566. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  567. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  568. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  569. data/lib/datadog/tracing/contrib/waterdrop.rb +42 -0
  570. data/lib/datadog/tracing/contrib.rb +9 -0
  571. data/lib/datadog/tracing/diagnostics/environment_logger.rb +4 -2
  572. data/lib/datadog/tracing/distributed/baggage.rb +64 -8
  573. data/lib/datadog/tracing/distributed/datadog.rb +13 -11
  574. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -14
  575. data/lib/datadog/tracing/distributed/propagation.rb +35 -3
  576. data/lib/datadog/tracing/distributed/trace_context.rb +85 -62
  577. data/lib/datadog/tracing/event.rb +1 -1
  578. data/lib/datadog/tracing/ext.rb +9 -0
  579. data/lib/datadog/tracing/metadata/ext.rb +23 -1
  580. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  581. data/lib/datadog/tracing/pipeline.rb +1 -1
  582. data/lib/datadog/tracing/remote.rb +36 -13
  583. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  584. data/lib/datadog/tracing/sampling/priority_sampler.rb +16 -1
  585. data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
  586. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  587. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  588. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  589. data/lib/datadog/tracing/span.rb +1 -1
  590. data/lib/datadog/tracing/span_event.rb +2 -2
  591. data/lib/datadog/tracing/span_operation.rb +34 -10
  592. data/lib/datadog/tracing/sync_writer.rb +0 -1
  593. data/lib/datadog/tracing/trace_digest.rb +7 -0
  594. data/lib/datadog/tracing/trace_operation.rb +123 -16
  595. data/lib/datadog/tracing/tracer.rb +82 -23
  596. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  597. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  598. data/lib/datadog/tracing/transport/http.rb +15 -9
  599. data/lib/datadog/tracing/transport/io/client.rb +6 -9
  600. data/lib/datadog/tracing/transport/io/traces.rb +28 -34
  601. data/lib/datadog/tracing/transport/trace_formatter.rb +22 -0
  602. data/lib/datadog/tracing/transport/traces.rb +13 -81
  603. data/lib/datadog/tracing/workers.rb +2 -1
  604. data/lib/datadog/tracing/writer.rb +1 -1
  605. data/lib/datadog/version.rb +2 -2
  606. data/lib/datadog.rb +8 -1
  607. metadata +185 -36
  608. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  609. data/ext/libdatadog_api/macos_development.md +0 -26
  610. data/lib/datadog/appsec/configuration/settings.rb +0 -400
  611. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
  612. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  613. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  614. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  615. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  616. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  617. data/lib/datadog/di/configuration/settings.rb +0 -212
  618. data/lib/datadog/di/transport/http/api.rb +0 -42
  619. data/lib/datadog/di/transport/http/client.rb +0 -47
  620. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
  621. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  622. data/lib/datadog/tracing/transport/http/api.rb +0 -44
  623. data/lib/datadog/tracing/workers/trace_writer.rb +0 -199
@@ -10,18 +10,9 @@ module Datadog
10
10
  class Gateway
11
11
  def initialize
12
12
  @middlewares = Hash.new { |h, k| h[k] = [] }
13
- @pushed_events = {}
14
13
  end
15
14
 
16
- # NOTE: Be careful with pushed names because every pushed event name
17
- # is recorded in order to provide an ability to any subscriber
18
- # to check wether an arbitrary event had happened.
19
- #
20
- # WARNING: If we start pushing generated names we should consider
21
- # limiting the storage of pushed names.
22
15
  def push(name, env, &block)
23
- @pushed_events[name] = true
24
-
25
16
  block ||= -> {}
26
17
  middlewares_for_name = @middlewares[name]
27
18
 
@@ -41,12 +32,8 @@ module Datadog
41
32
  stack.call(env)
42
33
  end
43
34
 
44
- def watch(name, key, &block)
45
- @middlewares[name] << Middleware.new(key, &block) unless @middlewares[name].any? { |m| m.key == key }
46
- end
47
-
48
- def pushed?(name)
49
- @pushed_events.key?(name)
35
+ def watch(name, &block)
36
+ @middlewares[name] << Middleware.new(&block)
50
37
  end
51
38
  end
52
39
 
@@ -5,14 +5,45 @@ module Datadog
5
5
  module Metrics
6
6
  # A class responsible for collecting WAF and RASP call metrics.
7
7
  class Collector
8
- Store = Struct.new(:evals, :matches, :errors, :timeouts, :duration_ns, :duration_ext_ns, keyword_init: true)
8
+ Store = Struct.new(
9
+ :evals,
10
+ :matches,
11
+ :errors,
12
+ :timeouts,
13
+ :duration_ns,
14
+ :duration_ext_ns,
15
+ :inputs_truncated,
16
+ :downstream_requests,
17
+ keyword_init: true
18
+ )
9
19
 
10
- attr_reader :waf, :rasp
20
+ DownstreamResponseStore = Struct.new(
21
+ :content_type_invalid,
22
+ :content_length_missing,
23
+ :content_length_too_big,
24
+ :content_exceed_content_length,
25
+ keyword_init: true
26
+ )
27
+
28
+ attr_reader :waf, :rasp, :downstream_responses
11
29
 
12
30
  def initialize
13
31
  @mutex = Mutex.new
14
- @waf = Store.new(evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0)
15
- @rasp = Store.new(evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0)
32
+
33
+ @waf = Store.new(
34
+ evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0,
35
+ duration_ext_ns: 0, inputs_truncated: 0, downstream_requests: 0
36
+ )
37
+
38
+ @rasp = Store.new(
39
+ evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0,
40
+ duration_ext_ns: 0, inputs_truncated: 0, downstream_requests: 0
41
+ )
42
+
43
+ @downstream_responses = DownstreamResponseStore.new(
44
+ content_type_invalid: 0, content_length_missing: 0,
45
+ content_length_too_big: 0, content_exceed_content_length: 0
46
+ )
16
47
  end
17
48
 
18
49
  def record_waf(result)
@@ -23,10 +54,11 @@ module Datadog
23
54
  @waf.timeouts += 1 if result.timeout?
24
55
  @waf.duration_ns += result.duration_ns
25
56
  @waf.duration_ext_ns += result.duration_ext_ns
57
+ @waf.inputs_truncated += 1 if result.input_truncated?
26
58
  end
27
59
  end
28
60
 
29
- def record_rasp(result)
61
+ def record_rasp(result, type:, phase: nil)
30
62
  @mutex.synchronize do
31
63
  @rasp.evals += 1
32
64
  @waf.matches += 1 if result.match?
@@ -34,8 +66,14 @@ module Datadog
34
66
  @rasp.timeouts += 1 if result.timeout?
35
67
  @rasp.duration_ns += result.duration_ns
36
68
  @rasp.duration_ext_ns += result.duration_ext_ns
69
+ @rasp.inputs_truncated += 1 if result.input_truncated?
70
+ @rasp.downstream_requests += 1 if type == Ext::RASP_SSRF && phase == Ext::RASP_REQUEST_PHASE
37
71
  end
38
72
  end
73
+
74
+ def record_ignored_downstream_response_body(reason)
75
+ @mutex.synchronize { @downstream_responses[reason] += 1 }
76
+ end
39
77
  end
40
78
  end
41
79
  end
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module AppSec
5
5
  module Metrics
6
- # A class responsible for exporting WAF and RASP call metrics.
6
+ # A class responsible for exporting WAF and RASP call metrics
7
7
  module Exporter
8
8
  module_function
9
9
 
@@ -22,11 +22,24 @@ module Datadog
22
22
  span.set_tag('_dd.appsec.rasp.timeout', 1) unless metrics.timeouts.zero?
23
23
  span.set_tag('_dd.appsec.rasp.duration', convert_ns_to_us(metrics.duration_ns))
24
24
  span.set_tag('_dd.appsec.rasp.duration_ext', convert_ns_to_us(metrics.duration_ext_ns))
25
+
26
+ # NOTE: In case of downstream requests being analyzed additionally
27
+ # with `Context.run_waf` method, we would need to share it
28
+ # between two exporting methods
29
+ unless metrics.downstream_requests.zero?
30
+ span.set_tag('_dd.appsec.downstream_request', metrics.downstream_requests)
31
+ end
25
32
  end
26
33
 
27
- # private
34
+ def export_downstream_response_metrics(metrics, span)
35
+ metrics.each_pair do |reason, count|
36
+ next if count.zero?
37
+
38
+ span.set_tag("_dd.appsec.downstream_request.response_body_ignored.#{reason}", count)
39
+ end
40
+ end
28
41
 
29
- def convert_ns_to_us(value)
42
+ private_class_method def convert_ns_to_us(value)
30
43
  value / 1000.0
31
44
  end
32
45
  end
@@ -5,17 +5,34 @@ module Datadog
5
5
  module Metrics
6
6
  # A class responsible for reporting WAF and RASP telemetry metrics.
7
7
  module Telemetry
8
+ ACTION_BLOCK = 'block_request'
9
+ ACTION_REDIRECT = 'redirect_request'
10
+ BLOCK_SUCCESS = 'success'
11
+ BLOCK_IRRELEVANT = 'irrelevant'
12
+
8
13
  module_function
9
14
 
10
- def report_rasp(type, result)
15
+ def report_rasp(type, result, phase: nil)
11
16
  return if result.error?
12
17
 
13
- tags = {rule_type: type, waf_version: Datadog::AppSec::WAF::VERSION::BASE_STRING}
18
+ tags = {rule_type: type, waf_version: WAF::VERSION::BASE_STRING}
19
+ tags[:rule_variant] = phase if phase
20
+
21
+ context = AppSec.active_context
22
+ tags[:event_rules_version] = context.waf_runner_ruleset_version if context
23
+
14
24
  namespace = Ext::TELEMETRY_METRICS_NAMESPACE
15
25
 
16
26
  AppSec.telemetry.inc(namespace, 'rasp.rule.eval', 1, tags: tags)
17
- AppSec.telemetry.inc(namespace, 'rasp.rule.match', 1, tags: tags) if result.match?
18
27
  AppSec.telemetry.inc(namespace, 'rasp.timeout', 1, tags: tags) if result.timeout?
28
+
29
+ if result.match?
30
+ blocked = result.actions.key?(ACTION_BLOCK) || result.actions.key?(ACTION_REDIRECT)
31
+ # NOTE: Mutates tags to avoid an extra hash allocation. Keep this the last .inc call.
32
+ tags[:block] = blocked ? BLOCK_SUCCESS : BLOCK_IRRELEVANT
33
+
34
+ AppSec.telemetry.inc(namespace, 'rasp.rule.match', 1, tags: tags)
35
+ end
19
36
  end
20
37
  end
21
38
  end
@@ -18,10 +18,29 @@ module Datadog
18
18
  waf_timeout: metrics.timeouts.positive?.to_s,
19
19
  request_blocked: context.interrupted?.to_s,
20
20
  block_failure: 'false',
21
- rate_limited: (!context.trace.sampled?).to_s
21
+ rate_limited: (!context.trace.sampled?).to_s,
22
+ input_truncated: metrics.inputs_truncated.positive?.to_s,
22
23
  }
23
24
  )
24
25
  end
26
+
27
+ def export_api_security_metrics(context)
28
+ web_framework = context.state[:web_framework]
29
+ return unless web_framework
30
+
31
+ if context.span&.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE).nil?
32
+ AppSec.telemetry.inc(
33
+ AppSec::Ext::TELEMETRY_METRICS_NAMESPACE, 'api_security.missing_route', 1,
34
+ tags: {framework: web_framework}
35
+ )
36
+ end
37
+
38
+ metric_name = context.state[:schema_extracted] ? 'schema' : 'no_schema'
39
+ AppSec.telemetry.inc(
40
+ AppSec::Ext::TELEMETRY_METRICS_NAMESPACE, "api_security.request.#{metric_name}", 1,
41
+ tags: {framework: web_framework}
42
+ )
43
+ end
25
44
  end
26
45
  end
27
46
  end
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'metrics/collector'
4
+ require_relative 'metrics/exporter'
5
+ require_relative 'metrics/telemetry'
6
+ require_relative 'metrics/telemetry_exporter'
7
+
3
8
  module Datadog
4
9
  module AppSec
5
10
  # This namespace contains classes related to metrics collection and exportation.
@@ -7,8 +12,3 @@ module Datadog
7
12
  end
8
13
  end
9
14
  end
10
-
11
- require_relative 'metrics/collector'
12
- require_relative 'metrics/exporter'
13
- require_relative 'metrics/telemetry'
14
- require_relative 'metrics/telemetry_exporter'
@@ -24,8 +24,9 @@ module Datadog
24
24
  end
25
25
 
26
26
  def watch_user_id(gateway = Instrumentation.gateway)
27
- gateway.watch('identity.set_user', :appsec) do |stack, user|
27
+ gateway.watch('identity.set_user') do |stack, user|
28
28
  context = AppSec.active_context
29
+ context.state[:has_identity_event] = true
29
30
 
30
31
  if user.id.nil? && user.login.nil? && user.session_id.nil?
31
32
  Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
@@ -39,14 +40,14 @@ module Datadog
39
40
 
40
41
  result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
41
42
 
42
- if result.match? || result.derivatives.any?
43
+ if result.match? || result.attributes.any?
43
44
  context.events.push(
44
45
  AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
45
46
  )
46
47
  end
47
48
 
48
49
  if result.match?
49
- AppSec::Event.tag_and_keep!(context, result)
50
+ AppSec::Event.tag(context, result)
50
51
  AppSec::ActionsHandler.handle(result.actions)
51
52
  end
52
53
 
@@ -55,22 +56,23 @@ module Datadog
55
56
  end
56
57
 
57
58
  def watch_user_login(gateway = Instrumentation.gateway)
58
- gateway.watch('appsec.events.user_lifecycle', :appsec) do |stack, kind|
59
+ gateway.watch('appsec.events.user_lifecycle') do |stack, kind|
59
60
  context = AppSec.active_context
61
+ context.state[:has_identity_event] = true
60
62
 
61
63
  next stack.call(kind) unless WATCHED_LOGIN_EVENTS.include?(kind)
62
64
 
63
65
  persistent_data = {"server.business_logic.#{kind}" => ARBITRARY_VALUE}
64
66
  result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
65
67
 
66
- if result.match? || result.derivatives.any?
68
+ if result.match? || result.attributes.any?
67
69
  context.events.push(
68
70
  AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
69
71
  )
70
72
  end
71
73
 
72
74
  if result.match?
73
- AppSec::Event.tag_and_keep!(context, result)
75
+ AppSec::Event.tag(context, result)
74
76
  AppSec::ActionsHandler.handle(result.actions)
75
77
  end
76
78
 
@@ -7,24 +7,30 @@ module Datadog
7
7
  module AppSec
8
8
  # Remote
9
9
  module Remote
10
- class ReadError < StandardError; end
11
-
12
10
  class NoRulesError < StandardError; end
13
11
 
14
12
  class << self
15
- CAP_ASM_RESERVED_1 = 1 << 0 # RESERVED
16
- CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
17
- CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
18
- CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
19
- CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
20
- CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
21
- CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
22
- CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
23
- CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
- CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
- CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
26
- CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
27
- CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
13
+ CAP_ASM_RESERVED_1 = 1 << 0
14
+ CAP_ASM_ACTIVATION = 1 << 1
15
+ CAP_ASM_IP_BLOCKING = 1 << 2
16
+ CAP_ASM_DD_RULES = 1 << 3
17
+ CAP_ASM_EXCLUSIONS = 1 << 4
18
+ CAP_ASM_REQUEST_BLOCKING = 1 << 5
19
+ CAP_ASM_RESPONSE_BLOCKING = 1 << 6
20
+ CAP_ASM_USER_BLOCKING = 1 << 7
21
+ CAP_ASM_CUSTOM_RULES = 1 << 8
22
+ CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9
23
+ CAP_ASM_TRUSTED_IPS = 1 << 10
24
+ CAP_ASM_PROCESSOR_OVERRIDES = 1 << 16
25
+ CAP_ASM_CUSTOM_DATA_SCANNERS = 1 << 17
26
+ CAP_ASM_RASP_SSRF = 1 << 23
27
+ CAP_ASM_RASP_SQLI = 1 << 21
28
+ CAP_ASM_AUTO_USER_INSTRUM_MODE = 1 << 31
29
+ CAP_ASM_ENDPOINT_FINGERPRINT = 1 << 32
30
+ CAP_ASM_SESSION_FINGERPRINT = 1 << 33
31
+ CAP_ASM_NETWORK_FINGERPRINT = 1 << 34
32
+ CAP_ASM_HEADER_FINGERPRINT = 1 << 35
33
+ CAP_ASM_TRACE_TAGGING_RULES = 1 << 43
28
34
 
29
35
  # TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
30
36
  ASM_CAPABILITIES = [
@@ -37,8 +43,16 @@ module Datadog
37
43
  CAP_ASM_CUSTOM_RULES,
38
44
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE,
39
45
  CAP_ASM_TRUSTED_IPS,
46
+ CAP_ASM_PROCESSOR_OVERRIDES,
47
+ CAP_ASM_CUSTOM_DATA_SCANNERS,
40
48
  CAP_ASM_RASP_SSRF,
41
49
  CAP_ASM_RASP_SQLI,
50
+ CAP_ASM_AUTO_USER_INSTRUM_MODE,
51
+ CAP_ASM_ENDPOINT_FINGERPRINT,
52
+ CAP_ASM_SESSION_FINGERPRINT,
53
+ CAP_ASM_NETWORK_FINGERPRINT,
54
+ CAP_ASM_HEADER_FINGERPRINT,
55
+ CAP_ASM_TRACE_TAGGING_RULES,
42
56
  ].freeze
43
57
 
44
58
  ASM_PRODUCTS = [
@@ -61,19 +75,26 @@ module Datadog
61
75
 
62
76
  matcher = Core::Remote::Dispatcher::Matcher::Product.new(ASM_PRODUCTS)
63
77
  receiver = Core::Remote::Dispatcher::Receiver.new(matcher) do |repository, changes|
64
- next unless AppSec.security_engine
78
+ engine = AppSec.security_engine
79
+ next unless engine
80
+
81
+ # We must process delete operations before insert and update operations
82
+ # to prevent duplicate-rule errors when the config name changes
83
+ changes.each do |change|
84
+ next unless change.type == :delete
85
+
86
+ engine.remove_config_at_path(change.path.to_s)
87
+ end
65
88
 
66
89
  changes.each do |change|
67
90
  content = repository[change.path]
68
- next unless content || change.type == :delete
91
+ next unless content
69
92
 
70
93
  case change.type
71
94
  when :insert, :update
72
- AppSec.security_engine.add_or_update_config(parse_content(content), path: change.path.to_s) # steep:ignore
73
-
74
- content.applied # steep:ignore
75
- when :delete
76
- AppSec.security_engine.remove_config_at_path(change.path.to_s) # steep:ignore
95
+ # @type var content: Core::Remote::Configuration::Content
96
+ engine.add_or_update_config(parse_content(content), path: change.path.to_s)
97
+ content.applied
77
98
  end
78
99
  end
79
100
 
@@ -93,13 +114,7 @@ module Datadog
93
114
  end
94
115
 
95
116
  def parse_content(content)
96
- data = content.data.read
97
-
98
- content.data.rewind
99
-
100
- raise ReadError, 'EOF reached' if data.nil?
101
-
102
- JSON.parse(data)
117
+ JSON.parse(content.data)
103
118
  end
104
119
  end
105
120
  end
@@ -7,6 +7,8 @@ module Datadog
7
7
  module AppSec
8
8
  # AppSec response
9
9
  class Response
10
+ SECURITY_RESPONSE_ID_PLACEHOLDER = '[security_response_id]'
11
+
10
12
  attr_reader :status, :headers, :body
11
13
 
12
14
  def initialize(status:, headers: {}, body: [])
@@ -37,16 +39,26 @@ module Datadog
37
39
  Response.new(
38
40
  status: interrupt_params['status_code']&.to_i || 403,
39
41
  headers: {'Content-Type' => content_type},
40
- body: [content(content_type)],
42
+ body: [
43
+ content(
44
+ security_response_id: interrupt_params['security_response_id'],
45
+ content_type: content_type
46
+ )
47
+ ],
41
48
  )
42
49
  end
43
50
 
44
51
  def redirect_response(interrupt_params)
45
52
  status_code = interrupt_params['status_code'].to_i
53
+ location = interrupt_params.fetch('location')
54
+
55
+ if (security_response_id = interrupt_params.fetch('security_response_id'))
56
+ location.gsub!(SECURITY_RESPONSE_ID_PLACEHOLDER, security_response_id)
57
+ end
46
58
 
47
59
  Response.new(
48
60
  status: ((status_code >= 300 && status_code < 400) ? status_code : 303),
49
- headers: {'Location' => interrupt_params.fetch('location')},
61
+ headers: {'Location' => location},
50
62
  body: [],
51
63
  )
52
64
  end
@@ -82,16 +94,18 @@ module Datadog
82
94
  DEFAULT_CONTENT_TYPE
83
95
  end
84
96
 
85
- def content(content_type)
97
+ def content(security_response_id:, content_type:)
86
98
  content_format = CONTENT_TYPE_TO_FORMAT[content_type]
87
99
 
88
100
  using_default = Datadog.configuration.appsec.block.templates.using_default?(content_format)
89
101
 
90
- if using_default
102
+ template = if using_default
91
103
  Datadog::AppSec::Assets.blocked(format: content_format)
92
104
  else
93
105
  Datadog.configuration.appsec.block.templates.send(content_format)
94
106
  end
107
+
108
+ template.gsub(SECURITY_RESPONSE_ID_PLACEHOLDER, security_response_id.to_s)
95
109
  end
96
110
  end
97
111
  end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'route_text'
4
+ require_relative 'route_pattern'
5
+
6
+ module Datadog
7
+ module AppSec
8
+ module RouteNormalizer
9
+ # Normalizes Rails route patterns into route format, inspired by
10
+ # OpenAPI v3 path templating
11
+ #
12
+ # NOTE: Uses the parsed Journey AST when available
13
+ #
14
+ # @api private
15
+ class RailsRoutePattern
16
+ DOT_CHAR = '.'
17
+ GROUP_OPEN_CHAR = '('
18
+ OPTIONAL_GROUP_PATTERN = /\(([^()]*)\)/
19
+ NAMED_PARAM_PREFIX_CHAR = ':'
20
+ GLOB_PARAM_PREFIX_CHAR = '*'
21
+
22
+ def initialize(pattern)
23
+ @pattern = pattern
24
+ end
25
+
26
+ def normalize(path_params:, path:)
27
+ @path_params = path_params
28
+ @path = path
29
+
30
+ if @pattern.is_a?(String)
31
+ # NOTE: Journey groups without route params are never kept
32
+ # Example: `/foo(/bar)` with request `/foo/bar` normalizes to `/foo`
33
+ pattern = remove_paramless_optional_groups(@pattern)
34
+ return RoutePattern.new(pattern).normalize(path: path)
35
+ end
36
+
37
+ route_path = @pattern.path
38
+ route_spec = route_path.spec
39
+
40
+ unless route_spec.respond_to?(:type)
41
+ return RoutePattern.new(route_spec.to_s).normalize(path: path)
42
+ end
43
+
44
+ if route_path.names.empty?
45
+ route_string = route_spec.to_s
46
+ return RouteText.escape(route_string) unless route_string.include?(GROUP_OPEN_CHAR)
47
+ end
48
+
49
+ buffer = Buffer.new
50
+ visit_route_node(route_spec, buffer)
51
+ buffer.to_path
52
+ end
53
+
54
+ private
55
+
56
+ def remove_paramless_optional_groups(pattern)
57
+ result = pattern
58
+
59
+ loop do
60
+ substituted = result.gsub(OPTIONAL_GROUP_PATTERN) do
61
+ # NOTE: OPTIONAL_GROUP_PATTERN always captures a string for each gsub match
62
+ group = ::Regexp.last_match(1) # : String
63
+ optional_group_has_route_params?(group) ? "(#{group})" : ''
64
+ end
65
+
66
+ return result if substituted == result
67
+
68
+ result = substituted
69
+ end
70
+ end
71
+
72
+ def visit_route_node(node, buffer)
73
+ case node.type
74
+ when :CAT
75
+ visit_route_node(node.left, buffer)
76
+ visit_route_node(node.right, buffer)
77
+ when :SLASH
78
+ buffer.flush
79
+ when :LITERAL
80
+ buffer.add_literal(node.left)
81
+ when :DOT
82
+ buffer.add_literal(DOT_CHAR)
83
+ when :SYMBOL, :STAR
84
+ buffer.add_param(node.name)
85
+ when :GROUP
86
+ visit_route_node(node.left, buffer) if group_present?(node.left)
87
+ end
88
+ end
89
+
90
+ def group_present?(node)
91
+ param_names = collect_group_param_names(node, [])
92
+ return false if param_names.empty?
93
+
94
+ param_names.all? { |name| param_matched_path?(name.to_sym) }
95
+ end
96
+
97
+ def param_matched_path?(name)
98
+ return @path_params[name].is_a?(String) unless name == :format
99
+
100
+ format = @path_params[:format]
101
+ return false if !format.is_a?(String) || format.empty?
102
+
103
+ dot_index = @path.length - format.length - 1
104
+ return false if dot_index < 0 || @path[dot_index] != DOT_CHAR
105
+
106
+ @path.end_with?(format)
107
+ end
108
+
109
+ def collect_group_param_names(node, memo)
110
+ case node.type
111
+ when :SYMBOL, :STAR
112
+ memo << node.name
113
+ when :CAT
114
+ collect_group_param_names(node.left, memo)
115
+ collect_group_param_names(node.right, memo)
116
+ end
117
+
118
+ memo
119
+ end
120
+
121
+ def optional_group_has_route_params?(group)
122
+ group.include?(NAMED_PARAM_PREFIX_CHAR) || group.include?(GLOB_PARAM_PREFIX_CHAR)
123
+ end
124
+
125
+ class Buffer
126
+ def initialize
127
+ @segments = []
128
+ @text = +''
129
+ @params = []
130
+ @nameless_param_count = 0
131
+ end
132
+
133
+ def add_literal(text)
134
+ @text << text
135
+ end
136
+
137
+ def add_param(name)
138
+ @params << name
139
+ end
140
+
141
+ def flush
142
+ return if @text.empty? && @params.empty?
143
+
144
+ @segments << if @params.empty?
145
+ RouteText.escape(@text)
146
+ else
147
+ render_params(@params)
148
+ end
149
+
150
+ @text = +''
151
+ @params.clear
152
+ end
153
+
154
+ def to_path
155
+ flush
156
+
157
+ "/#{@segments.join('/')}"
158
+ end
159
+
160
+ private
161
+
162
+ def render_params(params)
163
+ names = params.map do |name| # $ String
164
+ next name unless name.empty?
165
+
166
+ @nameless_param_count += 1
167
+ "param#{@nameless_param_count}"
168
+ end
169
+
170
+ "{#{names.join('+')}}"
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end