Module: Datadog::Tracing::Contrib::Sequel::JDBCConnectionString

Defined in:
lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb

Overview

Extracts connection metadata from JDBC connection strings.

Class Method Summary collapse

Class Method Details

.parse(connection_string) ⇒ Hash{Symbol => String, nil}

The returned Hash guarantees the existence of all keys, but Hash values can be nil when not parseable from the connection string.

Parameters:

  • connection_string (String, nil) —

    the JDBC connection string to parse

Returns:

  • (Hash{Symbol => String, nil}) —
    • :host — host value when the subname uses authority syntax: //host[:port]
    • :port — port when the subname uses authority syntax: //host[:port]
    • :database — best-effort database name


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb', line 39

def parse(connection_string)
  # @type var result: metadata
  result = {host: nil, port: nil, database: nil}
  return result unless connection_string.is_a?(String) && connection_string.valid_encoding?

  if connection_string.bytesize > MAX_BYTES
    # Strip userinfo before truncation. Otherwise, an `@` beyond the limit could be
    # discarded and leave a credential prefix looking like a valid authority/host.
    authority_marker = connection_string.index("//")
    if authority_marker
      authority_start = authority_marker + 2
      authority_end = [
        connection_string.index("/", authority_start),
        connection_string.index(";", authority_start),
        connection_string.index("?", authority_start),
      ].compact.min || connection_string.length
      userinfo_end = connection_string.rindex("@", authority_end - 1)

      if userinfo_end && userinfo_end >= authority_start
        connection_string =
          # Steep: https://github.com/soutaro/steep/issues/1219
          connection_string[0...authority_start] + # steep:ignore NoMethod
          connection_string[(userinfo_end + 1)..-1].to_s
      end
    end

    # Keep one extra byte, then let `chop` safely remove multi-byte unicode characters.
    if connection_string.bytesize > MAX_BYTES
      connection_string = connection_string.byteslice(0, MAX_BYTES + 1).chop
    end
  end

  match = CONNECTION_STRING_PATTERN.match(connection_string)
  return result unless match

  # We start with: `host[:port][/database][;properties][?query]`.
  subname = match[:subname]

  # Extract `query` from the end, leaving `host[:port][/database][;properties]`.
  subname, query_separator, query = subname.partition("?")
  query = nil if query_separator.empty?

  # Extract `properties` next, leaving `host[:port][/database]`.
  subname, properties_separator, properties = subname.partition(";")
  properties = nil if properties_separator.empty?

  # Separate the authority (`host[:port]`) from the optional database path.
  authority, path = subname.split("/", 2)
  return result if authority.nil? || authority.empty?

  host, port = host_and_port_from_authority(authority)
  return result unless host

  database = database_from_path(path) ||
    database_from_properties(properties) || database_from_properties(query)

  {host: host, port: port, database: database}
rescue Encoding::CompatibilityError, ArgumentError
  result
end