diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParser.java index a908e0766d2..b4c01027609 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParser.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -116,6 +116,12 @@ DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { } } else { paramLoc = jdbcUrl.indexOf(';'); + if (paramLoc >= 0 && paramLoc < hostIndex) { + // the "://" is inside a property value (e.g. db2:mydb;x=file://...), so there is no host + // part to parse, but the properties (user, schema, ...) still apply + populateStandardProperties(builder, splitQuery(jdbcUrl.substring(paramLoc + 1), ';')); + return builder; + } urlPart1 = paramLoc >= 0 ? jdbcUrl.substring(0, paramLoc) : jdbcUrl; urlPart2 = paramLoc >= 0 ? jdbcUrl.substring(paramLoc + 1) : null; } diff --git a/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParserDB2Test.java b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParserDB2Test.java index 091c1e2b02f..9a222676336 100644 --- a/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParserDB2Test.java +++ b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionUrlParserDB2Test.java @@ -1,7 +1,9 @@ package datadog.trace.bootstrap.instrumentation.jdbc; +import static datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionUrlParser.DB2; import static datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionUrlParser.extractDBInfo; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import org.tabletest.junit.TableTest; @@ -32,4 +34,42 @@ void db2UrlWithEqualsAndNoPortShouldParseCorrectly( assertEquals(user, info.getUser()); assertEquals(db, info.getDb()); } + + /** + * A URL without "//" after the type, whose only "://" is inside a ';' property value, used to + * throw a {@code StringIndexOutOfBoundsException}. Calls {@code DB2.doParse} directly since + * {@code extractDBInfo} swallows parse exceptions. + */ + @TableTest({ + "scenario | url | type ", + "DB2 with URL in property value | db2:mydb;x=http://y | db2 ", + "AS400 with file URL in property | as400:host;ssltruststore=file://x | as400", + "AS400 with several properties | as400:host;libraries=a;secure=true;keystore=file:///x | as400", + "Empty type suffix with property | db2:;a=b://h | db2 " + }) + void schemeSeparatorOnlyInPropertyValueShouldNotThrow(String url, String type) { + DBInfo info = DB2.doParse(url, DBInfo.DEFAULT.toBuilder().type(type)).build(); + assertEquals(type, info.getType()); + assertNull(info.getHost()); + assertEquals(50000, info.getPort()); + assertNull(info.getInstance()); + } + + /** + * The same URLs keep their recognized properties. The old parser applied them before throwing, + * and {@code parse} returned that builder from its catch block, so they were recorded then too. + */ + @TableTest({ + "scenario | url | type | user | schema", + "AS400 user before file URL | as400:host;user=alice;ssltruststore=file://x | as400 | alice | ", + "DB2 schema before http URL | db2:mydb;schema=s1;x=http://y | db2 | | s1 ", + "AS400 user after file URL | as400:host;keystore=file:///k;user=bob | as400 | bob | " + }) + void schemeSeparatorOnlyInPropertyValueKeepsProperties( + String url, String type, String user, String schema) { + DBInfo info = DB2.doParse(url, DBInfo.DEFAULT.toBuilder().type(type)).build(); + assertEquals(type, info.getType()); + assertEquals(user, info.getUser()); + assertEquals(schema, info.getSchema()); + } }