Skip to content

Commit

Permalink
Merge pull request #2054 from ClickHouse/fix_user_agent
Browse files Browse the repository at this point in the history
[client-v2] Fix user agent
  • Loading branch information
chernser authored Dec 27, 2024
2 parents 5f3e7fd + 262251c commit 2703853
Show file tree
Hide file tree
Showing 24 changed files with 372 additions and 106 deletions.
6 changes: 6 additions & 0 deletions clickhouse-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.clickhouse.client.config;

import java.io.InputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.clickhouse.config.ClickHouseOption;
import com.clickhouse.data.ClickHouseChecker;
Expand Down Expand Up @@ -491,19 +493,8 @@ public enum ClickHouseClientOption implements ClickHouseOption {
}
options = Collections.unmodifiableMap(map);

// <artifact-id> <version> (revision: <revision>)
String ver = ClickHouseClientOption.class.getPackage().getImplementationVersion();
String[] parts = ver == null || ver.isEmpty() ? null : ver.split("\\s");
if (parts != null && parts.length == 4 && parts[1].length() > 0 && parts[3].length() > 1
&& ver.charAt(ver.length() - 1) == ')') {
PRODUCT_VERSION = parts[1];
ver = parts[3];
PRODUCT_REVISION = ver.substring(0, ver.length() - 1);
} else { // perhaps try harder by checking version from pom.xml?
PRODUCT_VERSION = LATEST_KNOWN_VERSION;
PRODUCT_REVISION = UNKNOWN;
}

PRODUCT_VERSION = readVersionFromResource("clickhouse-client-version.properties");
PRODUCT_REVISION = UNKNOWN;

CLIENT_OS_INFO = new StringBuilder().append(getSystemConfig("os.name", "O/S")).append('/')
.append(getSystemConfig("os.version", UNKNOWN)).toString();
Expand All @@ -514,12 +505,31 @@ public enum ClickHouseClientOption implements ClickHouseOption {
CLIENT_JVM_INFO = new StringBuilder().append(getSystemConfig("java.vm.name", "Java")).append('/')
.append(javaVersion).toString();
CLIENT_USER = getSystemConfig("user.name", UNKNOWN);

String host = null;
try {
ver = InetAddress.getLocalHost().getHostName();
host = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e1) {
// ignore
}
CLIENT_HOST = ver == null || ver.isEmpty() ? UNKNOWN : ver;
CLIENT_HOST = host == null || host.isEmpty() ? UNKNOWN : host;
}

public static String readVersionFromResource(String resourceFilePath) {
// TODO: move to client-v2 when client-v1 is deprecated completely
String tmpVersion = "unknown";
try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceFilePath)) {
Properties p = new Properties();
p.load(in);

String tmp = p.getProperty("version");
if (tmp != null && !tmp.isEmpty() && !tmp.equals("${revision}")) {
tmpVersion = tmp;
}
} catch (Exception e) {
// ignore
}
return tmpVersion;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version=${revision}
build.date=${build_timestamp}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ClickHouseConfigTest {
@Test(groups = { "unit" })
Expand Down Expand Up @@ -74,7 +76,8 @@ public void testCustomValues() {
@Test(groups = { "unit" })
public void testClientInfo() throws UnknownHostException {
ClickHouseConfig config = new ClickHouseConfig();
Assert.assertEquals(config.getProductVersion(), ClickHouseClientOption.LATEST_KNOWN_VERSION);
Matcher versioMatcher = Pattern.compile("(^|\\\\.[\\\\d]+)+.*").matcher(config.getProductVersion());
Assert.assertTrue(versioMatcher.matches());
Assert.assertEquals(config.getProductRevision(), "unknown");
Assert.assertEquals(config.getClientOsInfo(),
System.getProperty("os.name") + "/" + System.getProperty("os.version"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version=${revision}
build.date=${build_timestamp}
6 changes: 6 additions & 0 deletions client-v2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
56 changes: 17 additions & 39 deletions client-v2/src/main/java/com/clickhouse/client/api/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
import com.clickhouse.client.api.internal.ClickHouseLZ4OutputStream;
import com.clickhouse.client.api.internal.ClientStatisticsHolder;
import com.clickhouse.client.api.internal.ClientV1AdaptorHelper;
import com.clickhouse.client.api.internal.EnvUtils;
import com.clickhouse.client.api.internal.HttpAPIClientHelper;
import com.clickhouse.client.api.internal.MapUtils;
import com.clickhouse.client.api.internal.ServerSettings;
import com.clickhouse.client.api.internal.SettingsConverter;
import com.clickhouse.client.api.internal.TableSchemaParser;
import com.clickhouse.client.api.internal.ValidationUtils;
Expand Down Expand Up @@ -76,8 +74,8 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TimeZone;
Expand Down Expand Up @@ -186,6 +184,7 @@ private Client(Set<String> endpoints, Map<String,String> configuration, boolean
}
this.columnToMethodMatchingStrategy = columnToMethodMatchingStrategy;


updateServerContext();
}

Expand Down Expand Up @@ -359,6 +358,9 @@ public Builder addEndpoint(Protocol protocol, String host, int port, boolean sec
*/
public Builder setOption(String key, String value) {
this.configuration.put(key, value);
if (key.equals(ClientConfigProperties.PRODUCT_NAME.getKey())) {
setClientName(value);
}
return this;
}

Expand Down Expand Up @@ -817,7 +819,7 @@ public Builder setSharedOperationExecutor(ExecutorService executorService) {
/**
* Set size of a buffers that are used to read/write data from the server. It is mainly used to copy data from
* a socket to application memory and visa-versa. Setting is applied for both read and write operations.
* Default is 8192 bytes.
* Default is 300,000 bytes.
*
* @param size - size in bytes
* @return
Expand Down Expand Up @@ -977,7 +979,9 @@ public Builder setClientName(String clientName) {
* @return same instance of the builder
*/
public Builder setOptions(Map<String, String> options) {
this.configuration.putAll(options);
for (Map.Entry<String, String> entry : options.entrySet()) {
setOption(entry.getKey(), entry.getValue());
}
return this;
}

Expand Down Expand Up @@ -1147,41 +1151,7 @@ private void setDefaults() {
if (!configuration.containsKey(ClientConfigProperties.USE_HTTP_COMPRESSION.getKey())) {
useHttpCompression(false);
}

String userAgent = configuration.getOrDefault(ClientConfigProperties.HTTP_HEADER_PREFIX + HttpHeaders.USER_AGENT.toUpperCase(Locale.US), "");
String clientName = configuration.getOrDefault(ClientConfigProperties.CLIENT_NAME.getKey(), "");
httpHeader(HttpHeaders.USER_AGENT, buildUserAgent(userAgent.isEmpty() ? clientName : userAgent));
}

private static String buildUserAgent(String customUserAgent) {

StringBuilder userAgent = new StringBuilder();
if (customUserAgent != null && !customUserAgent.isEmpty()) {
userAgent.append(customUserAgent).append(" ");
}

userAgent.append(CLIENT_USER_AGENT);

String clientVersion = Client.class.getPackage().getImplementationVersion();
if (clientVersion == null) {
clientVersion = LATEST_ARTIFACT_VERSION;
}
userAgent.append(clientVersion);

userAgent.append(" (");
userAgent.append(System.getProperty("os.name"));
userAgent.append("; ");
userAgent.append("jvm:").append(System.getProperty("java.version"));
userAgent.append("; ");

userAgent.setLength(userAgent.length() - 2);
userAgent.append(')');

return userAgent.toString();
}

public static final String LATEST_ARTIFACT_VERSION = "0.7.1-patch1";
public static final String CLIENT_USER_AGENT = "clickhouse-java-v2/";
}

private ClickHouseNode getServerNode() {
Expand Down Expand Up @@ -2170,6 +2140,10 @@ public String getServerVersion() {
return this.serverVersion;
}

public String getClientVersion() {
return clientVersion;
}

/**
* Sets list of DB roles that should be applied to each query.
*
Expand All @@ -2186,6 +2160,10 @@ public void updateClientName(String name) {
this.configuration.put(ClientConfigProperties.CLIENT_NAME.getKey(), name);
}

public static final String clientVersion =
ClickHouseClientOption.readVersionFromResource("client-v2-version.properties");
public static final String CLIENT_USER_AGENT = "clickhouse-java-v2/";

private Collection<String> unmodifiableDbRolesView = Collections.emptyList();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,16 @@ public enum ClientConfigProperties {

CLIENT_RETRY_ON_FAILURE("client_retry_on_failures"),

CLIENT_NAME("client_name");
CLIENT_NAME("client_name"),

/**
* An old alias to {@link ClientConfigProperties#CLIENT_NAME}. Using the last one is preferred.
*/
@Deprecated
PRODUCT_NAME("product_name"),


;

private String key;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ public Object getOption(String option) {
* @param option - configuration option name
* @param value - configuration option value
*/
public void setOption(String option, Object value) {
public InsertSettings setOption(String option, Object value) {
rawSettings.put(option, value);
if (option.equals(ClientConfigProperties.PRODUCT_NAME.getKey())) {
rawSettings.put(ClientConfigProperties.CLIENT_NAME.getKey(), value);
}
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.NoHttpResponseException;
import org.apache.hc.core5.http.config.CharCodingConfig;
Expand Down Expand Up @@ -73,9 +74,6 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

import static com.clickhouse.client.api.ClientConfigProperties.SOCKET_TCP_NO_DELAY_OPT;

public class HttpAPIClientHelper {
private static final Logger LOG = LoggerFactory.getLogger(Client.class);
Expand All @@ -92,14 +90,12 @@ public class HttpAPIClientHelper {

private final Set<ClientFaultCause> defaultRetryCauses;

private String httpClientUserAgentPart;
private String defaultUserAgent;

public HttpAPIClientHelper(Map<String, String> configuration) {
this.chConfiguration = configuration;
this.httpClient = createHttpClient();

this.httpClientUserAgentPart = this.httpClient.getClass().getPackage().getImplementationTitle() + "/" + this.httpClient.getClass().getPackage().getImplementationVersion();

RequestConfig.Builder reqConfBuilder = RequestConfig.custom();
MapUtils.applyLong(chConfiguration, "connection_request_timeout",
(t) -> reqConfBuilder
Expand All @@ -116,6 +112,8 @@ public HttpAPIClientHelper(Map<String, String> configuration) {
if (defaultRetryCauses.contains(ClientFaultCause.None)) {
defaultRetryCauses.removeIf(c -> c != ClientFaultCause.None);
}

this.defaultUserAgent = buildDefaultUserAgent();
}

/**
Expand Down Expand Up @@ -472,9 +470,9 @@ private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String,
}

// -- keep last
Header userAgent = req.getFirstHeader(HttpHeaders.USER_AGENT);
req.setHeader(HttpHeaders.USER_AGENT, userAgent == null ? httpClientUserAgentPart : userAgent.getValue() + " " + httpClientUserAgentPart);
correctUserAgentHeader(req, requestConfig);
}

private void addQueryParams(URIBuilder req, Map<String, String> chConfig, Map<String, Object> requestConfig) {
for (Map.Entry<String, String> entry : chConfig.entrySet()) {
if (entry.getKey().startsWith(ClientConfigProperties.SERVER_SETTING_PREFIX)) {
Expand Down Expand Up @@ -646,6 +644,54 @@ public static Map<String, String> parseUrlParameters(URL url) {
return params;
}


private void correctUserAgentHeader(HttpRequest request, Map<String, Object> requestConfig) {
//TODO: implement cache for user-agent
Header userAgentHeader = request.getLastHeader(HttpHeaders.USER_AGENT);
request.removeHeaders(HttpHeaders.USER_AGENT);

String clientName = chConfiguration.getOrDefault(ClientConfigProperties.CLIENT_NAME.getKey(), "");
if (requestConfig != null) {
String reqClientName = (String) requestConfig.get(ClientConfigProperties.CLIENT_NAME.getKey());
if (reqClientName != null && !reqClientName.isEmpty()) {
clientName = reqClientName;
}
}
String userAgentValue = defaultUserAgent;
if (userAgentHeader == null && clientName != null && !clientName.isEmpty()) {
userAgentValue = clientName + " " + defaultUserAgent;
} else if (userAgentHeader != null) {
userAgentValue = userAgentHeader.getValue() + " " + defaultUserAgent;
}

request.setHeader(HttpHeaders.USER_AGENT, userAgentValue);
}

private String buildDefaultUserAgent() {
StringBuilder userAgent = new StringBuilder();
userAgent.append(Client.CLIENT_USER_AGENT);

String clientVersion = Client.clientVersion;

userAgent.append(clientVersion);

userAgent.append(" (");
userAgent.append(System.getProperty("os.name"));
userAgent.append("; ");
userAgent.append("jvm:").append(System.getProperty("java.version"));
userAgent.append("; ");

userAgent.setLength(userAgent.length() - 2);
userAgent.append(')');

userAgent.append(" ")
.append(this.httpClient.getClass().getPackage().getImplementationTitle().replaceAll(" ", "-"))
.append('/')
.append(this.httpClient.getClass().getPackage().getImplementationVersion());

return userAgent.toString();
}

public void close() {
httpClient.close(CloseMode.IMMEDIATE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public QuerySettings() {
*/
public QuerySettings setOption(String option, Object value) {
rawSettings.put(option, value);
if (option.equals(ClientConfigProperties.PRODUCT_NAME.getKey())) {
rawSettings.put(ClientConfigProperties.CLIENT_NAME.getKey(), value);
}

return this;
}

Expand Down
Loading

0 comments on commit 2703853

Please sign in to comment.