Skip to content

Commit 17882a4

Browse files
committed
Add client metadata header
1 parent 24faa6a commit 17882a4

File tree

3 files changed

+177
-1
lines changed

3 files changed

+177
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.transport.rest_client;
21+
22+
// Copied verbatim from https://github.com/elastic/jvm-languages-sniffer
23+
24+
import java.lang.reflect.Field;
25+
import java.lang.reflect.Method;
26+
27+
class LanguageRuntimeVersions {
28+
29+
/**
30+
* Returns runtime information by looking up classes identifying non-Java JVM
31+
* languages and appending a key with their name and their major.minor version, if available
32+
*/
33+
public static String getRuntimeMetadata() {
34+
StringBuilder s = new StringBuilder();
35+
String version;
36+
37+
version = kotlinVersion();
38+
if (version != null) {
39+
s.append(",kt=").append(version);
40+
}
41+
42+
version = scalaVersion();
43+
if (version != null) {
44+
s.append(",sc=").append(version);
45+
}
46+
47+
version = clojureVersion();
48+
if (version != null) {
49+
s.append(",clj=").append(version);
50+
}
51+
52+
version = groovyVersion();
53+
if (version != null) {
54+
s.append(",gy=").append(version);
55+
}
56+
57+
version = jRubyVersion();
58+
if (version != null) {
59+
s.append(",jrb=").append(version);
60+
}
61+
62+
return s.toString();
63+
}
64+
65+
public static String kotlinVersion() {
66+
// KotlinVersion.CURRENT.toString()
67+
return keepMajorMinor(getStaticField("kotlin.KotlinVersion", "CURRENT"));
68+
}
69+
70+
public static String scalaVersion() {
71+
// scala.util.Properties.versionNumberString()
72+
return keepMajorMinor(callStaticMethod("scala.util.Properties", "versionNumberString"));
73+
}
74+
75+
public static String clojureVersion() {
76+
// (clojure-version) which translates to
77+
// clojure.core$clojure_version.invokeStatic()
78+
return keepMajorMinor(callStaticMethod("clojure.core$clojure_version", "invokeStatic"));
79+
}
80+
81+
public static String groovyVersion() {
82+
// groovy.lang.GroovySystem.getVersion()
83+
// There's also getShortVersion(), but only since Groovy 3.0.1
84+
return keepMajorMinor(callStaticMethod("groovy.lang.GroovySystem", "getVersion"));
85+
}
86+
87+
public static String jRubyVersion() {
88+
// org.jruby.runtime.Constants.VERSION
89+
return keepMajorMinor(getStaticField("org.jruby.runtime.Constants", "VERSION"));
90+
}
91+
92+
private static String getStaticField(String className, String fieldName) {
93+
Class<?> clazz;
94+
try {
95+
clazz = Class.forName(className);
96+
} catch (ClassNotFoundException e) {
97+
return null;
98+
}
99+
100+
try {
101+
Field field = clazz.getField(fieldName);
102+
return field.get(null).toString();
103+
} catch (Exception e) {
104+
return ""; // can't get version information
105+
}
106+
}
107+
108+
private static String callStaticMethod(String className, String methodName) {
109+
Class<?> clazz;
110+
try {
111+
clazz = Class.forName(className);
112+
} catch (ClassNotFoundException e) {
113+
return null;
114+
}
115+
116+
try {
117+
Method m = clazz.getMethod(methodName);
118+
return m.invoke(null).toString();
119+
} catch (Exception e) {
120+
return ""; // can't get version information
121+
}
122+
}
123+
124+
static String keepMajorMinor(String version) {
125+
if (version == null) {
126+
return null;
127+
}
128+
129+
int firstDot = version.indexOf('.');
130+
int secondDot = version.indexOf('.', firstDot + 1);
131+
if (secondDot < 0) {
132+
return version;
133+
} else {
134+
return version.substring(0, secondDot);
135+
}
136+
}
137+
}

java-client/src/main/java/co/elastic/clients/transport/rest_client/RestClientOptions.java

+39
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import co.elastic.clients.transport.TransportOptions;
2323
import co.elastic.clients.transport.Version;
24+
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
25+
import org.apache.http.util.VersionInfo;
2426
import org.elasticsearch.client.RequestOptions;
2527
import org.elasticsearch.client.WarningsHandler;
2628

@@ -107,6 +109,9 @@ public RequestOptions.Builder restClientRequestOptionsBuilder() {
107109

108110
@Override
109111
public TransportOptions.Builder addHeader(String name, String value) {
112+
if (name.equalsIgnoreCase(CLIENT_META)) {
113+
return this;
114+
}
110115
if (name.equalsIgnoreCase(USER_AGENT)) {
111116
// We must filter out our own user-agent from the options or they'll end up as multiple values for the header
112117
RequestOptions options = builder.build();
@@ -159,6 +164,7 @@ public RestClientOptions build() {
159164
}
160165

161166
private static final String USER_AGENT = "User-Agent";
167+
private static final String CLIENT_META = "X-Elastic-Client-Meta";
162168

163169
static RestClientOptions initialOptions() {
164170
String ua = String.format(
@@ -171,8 +177,41 @@ static RestClientOptions initialOptions() {
171177
return new RestClientOptions(
172178
RequestOptions.DEFAULT.toBuilder()
173179
.addHeader(USER_AGENT, ua)
180+
.addHeader(CLIENT_META, getClientMeta())
174181
.addHeader("Accept", RestClientTransport.JsonContentType.toString())
175182
.build()
176183
);
177184
}
185+
186+
private static String getClientMeta() {
187+
188+
VersionInfo httpClientVersion = null;
189+
try {
190+
httpClientVersion = VersionInfo.loadVersionInfo(
191+
"org.apache.http.nio.client",
192+
HttpAsyncClientBuilder.class.getClassLoader()
193+
);
194+
} catch (Exception e) {
195+
// Keep unknown
196+
}
197+
198+
// Use a single 'p' suffix for all prerelease versions (snapshot, beta, etc).
199+
String metaVersion = Version.VERSION == null ? "" : Version.VERSION.toString();
200+
int dashPos = metaVersion.indexOf('-');
201+
if (dashPos > 0) {
202+
metaVersion = metaVersion.substring(0, dashPos) + "p";
203+
}
204+
205+
// service, language, transport, followed by additional information
206+
return "es="
207+
+ metaVersion
208+
+ ",jv="
209+
+ System.getProperty("java.specification.version")
210+
+ ",hl=2"
211+
+ ",t="
212+
+ metaVersion
213+
+ ",hc="
214+
+ (httpClientVersion == null ? "" : httpClientVersion.getRelease())
215+
+ LanguageRuntimeVersions.getRuntimeMetadata();
216+
}
178217
}

java-client/src/test/java/co/elastic/clients/transport/RequestOptionsTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ public class RequestOptionsTest extends Assert {
4848
private static HttpServer httpServer;
4949
private static RestClient restClient;
5050

51-
5251
@Before
5352
public void classSetup() throws IOException {
5453

@@ -110,6 +109,7 @@ public void testDefaultHeaders() throws IOException {
110109

111110
assertTrue(props.getProperty("header-user-agent").startsWith("elastic-java/" + Version.VERSION.toString()));
112111
assertTrue(props.getProperty("header-x-elastic-client-meta").contains("es="));
112+
assertTrue(props.getProperty("header-x-elastic-client-meta").contains("hl=2"));
113113
assertEquals(
114114
"application/vnd.elasticsearch+json; compatible-with=" + String.valueOf(Version.VERSION.major()),
115115
props.getProperty("header-accept")

0 commit comments

Comments
 (0)