diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java b/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java index 958378107..59ecd30ca 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java @@ -12,6 +12,7 @@ package com.google.api.client.googleapis.services; +import com.google.api.client.googleapis.GoogleUtils; import com.google.api.client.googleapis.MethodOverride; import com.google.api.client.googleapis.batch.BatchCallback; import com.google.api.client.googleapis.batch.BatchRequest; @@ -37,6 +38,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Abstract Google client request for a {@link AbstractGoogleClient}. @@ -59,6 +62,8 @@ public abstract class AbstractGoogleClientRequest extends GenericData { */ public static final String USER_AGENT_SUFFIX = "Google-API-Java-Client"; + private static final String API_VERSION_HEADER = "X-Goog-Api-Client"; + /** Google client. */ private final AbstractGoogleClient abstractGoogleClient; @@ -119,6 +124,48 @@ protected AbstractGoogleClientRequest(AbstractGoogleClient abstractGoogleClient, } else { requestHeaders.setUserAgent(USER_AGENT_SUFFIX); } + // Set the header for the Api Client version (Java and OS version) + requestHeaders.set(API_VERSION_HEADER, ApiClientVersion.build(abstractGoogleClient)); + } + + /** + * Internal class to help build the X-Goog-Api-Client header. This header identifies the + * API Client version and environment. + * + * See + * + */ + private static class ApiClientVersion { + private static final String JAVA_VERSION = formatSemver(System.getProperty("java.version")); + private static final String OS_NAME = formatName(System.getProperty("os.name")); + private static final String OS_VERSION = formatSemver(System.getProperty("os.version")); + + private static String build(AbstractGoogleClient client) { + // TODO(chingor): add the API version from the generated client + return String.format( + "java/%s http-google-%s/%s %s/%s", + JAVA_VERSION, + formatName(client.getClass().getSimpleName()), + formatSemver(GoogleUtils.VERSION), + OS_NAME, + OS_VERSION + ); + } + + private static String formatName(String name) { + // Only lowercase letters, digits, and "-" are allowed + return name.toLowerCase().replaceAll("[^\\w\\d\\-]", "-"); + } + + private static String formatSemver(String version) { + // Take only the semver version: x.y.z-a_b_c -> x.y.z + Matcher m = Pattern.compile("(\\d+\\.\\d+\\.\\d+).*").matcher(version); + if (m.find()) { + return m.group(1); + } else { + return version; + } + } } /** Returns whether to disable GZip compression of HTTP content. */ diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java index c7599a1b8..4e5955cf1 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java @@ -175,7 +175,6 @@ public LowLevelHttpResponse execute() { public void testUserAgentSuffix() throws Exception { AssertUserAgentTransport transport = new AssertUserAgentTransport(); - // Specify an Application Name. String applicationName = "Test Application"; transport.expectedUserAgent = applicationName + " " @@ -187,17 +186,49 @@ public void testUserAgentSuffix() throws Exception { MockGoogleClientRequest request = new MockGoogleClientRequest(client, HttpMethods.GET, URI_TEMPLATE, null, Void.class); request.executeUnparsed(); + } + public void testUserAgent() throws Exception { + AssertUserAgentTransport transport = new AssertUserAgentTransport(); + transport.expectedUserAgent = AbstractGoogleClientRequest.USER_AGENT_SUFFIX + " " + HttpRequest.USER_AGENT_SUFFIX; // Don't specify an Application Name. - transport.expectedUserAgent = AbstractGoogleClientRequest.USER_AGENT_SUFFIX + " " - + HttpRequest.USER_AGENT_SUFFIX; - client = new MockGoogleClient.Builder( + MockGoogleClient client = new MockGoogleClient.Builder( + transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null).build(); + MockGoogleClientRequest request = + new MockGoogleClientRequest(client, HttpMethods.GET, URI_TEMPLATE, null, Void.class); + request.executeUnparsed(); + } + + public void testSetsApiClientHeader() throws Exception { + HttpTransport transport = new AssertHeaderTransport("X-Goog-Api-Client", "java/\\d+\\.\\d+\\.\\d+.*"); + MockGoogleClient client = new MockGoogleClient.Builder( transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null).build(); - request = + MockGoogleClientRequest request = new MockGoogleClientRequest(client, HttpMethods.GET, URI_TEMPLATE, null, Void.class); request.executeUnparsed(); } + private class AssertHeaderTransport extends MockHttpTransport { + String expectedHeader; + String expectedHeaderValue; + + AssertHeaderTransport(String header, String value) { + expectedHeader = header; + expectedHeaderValue = value; + } + + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + assertTrue(getFirstHeaderValue(expectedHeader).matches(expectedHeaderValue)); + return new MockLowLevelHttpResponse(); + } + }; + } + } + private class AssertUserAgentTransport extends MockHttpTransport { String expectedUserAgent;