Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allows user-agent header with header provider #871

Merged
merged 2 commits into from Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -77,7 +77,6 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.RateLimiter;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
Expand Down Expand Up @@ -161,6 +160,7 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -244,6 +244,8 @@ private void awaitTermination() throws InterruptedException {
private static final int GRPC_KEEPALIVE_SECONDS = 2 * 60;
private static final String USER_AGENT_KEY = "user-agent";
private static final String CLIENT_LIBRARY_LANGUAGE = "spanner-java";
public static final String DEFAULT_USER_AGENT =
CLIENT_LIBRARY_LANGUAGE + "/" + GaxProperties.getLibraryVersion(GapicSpannerRpc.class);

private final ManagedInstantiatingExecutorProvider executorProvider;
private boolean rpcIsClosed;
Expand Down Expand Up @@ -305,18 +307,11 @@ public GapicSpannerRpc(final SpannerOptions options) {
GaxGrpcProperties.getGrpcTokenName(), GaxGrpcProperties.getGrpcVersion())
.build();

HeaderProvider mergedHeaderProvider = options.getMergedHeaderProvider(internalHeaderProvider);
Map<String, String> headersWithUserAgent =
ImmutableMap.<String, String>builder()
.put(
USER_AGENT_KEY,
CLIENT_LIBRARY_LANGUAGE
+ "/"
+ GaxProperties.getLibraryVersion(GapicSpannerRpc.class))
.putAll(mergedHeaderProvider.getHeaders())
.build();
final HeaderProvider mergedHeaderProvider =
options.getMergedHeaderProvider(internalHeaderProvider);
final HeaderProvider headerProviderWithUserAgent =
FixedHeaderProvider.create(headersWithUserAgent);
headerProviderWithUserAgentFrom(mergedHeaderProvider);

this.metadataProvider =
SpannerMetadataProvider.create(
headerProviderWithUserAgent.getHeaders(),
Expand Down Expand Up @@ -494,6 +489,16 @@ public <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createUnaryCalla
}
}

private static HeaderProvider headerProviderWithUserAgentFrom(HeaderProvider headerProvider) {
final Map<String, String> headersWithUserAgent = new HashMap<>(headerProvider.getHeaders());
final String userAgent = headersWithUserAgent.get(USER_AGENT_KEY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thiagotnunes user-agent and User-Agent should be equivalent.
This needs case insensitivity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there, thanks for pointing this out.

This has been fixed in the following versions:

  1. 3.3.3
  2. 4.0.3
  3. 5.2.1
  4. 6.4.0

headersWithUserAgent.put(
USER_AGENT_KEY,
userAgent == null ? DEFAULT_USER_AGENT : userAgent + " " + DEFAULT_USER_AGENT);

return FixedHeaderProvider.create(headersWithUserAgent);
}

private static void checkEmulatorConnection(
SpannerOptions options,
TransportChannelProvider channelProvider,
Expand Down
Expand Up @@ -25,6 +25,7 @@

import com.google.api.core.ApiFunction;
import com.google.api.gax.rpc.ApiCallContext;
import com.google.api.gax.rpc.HeaderProvider;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.OAuth2Credentials;
import com.google.cloud.spanner.DatabaseAdminClient;
Expand Down Expand Up @@ -151,6 +152,7 @@ public class GapicSpannerRpcTest {
private Server server;
private InetSocketAddress address;
private final Map<SpannerRpc.Option, Object> optionsMap = new HashMap<>();
private Metadata seenHeaders;

@BeforeClass
public static void checkNotEmulator() {
Expand Down Expand Up @@ -183,6 +185,7 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
seenHeaders = headers;
String auth =
headers.get(Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER));
assertThat(auth).isEqualTo("Bearer " + VARIABLE_OAUTH_TOKEN);
Expand Down Expand Up @@ -502,6 +505,30 @@ public void testAdminRequestsLimitExceededRetryAlgorithm() {
assertThat(alg.shouldRetry(new Exception("random exception"), null)).isFalse();
}

@Test
public void testCustomUserAgent() {
final HeaderProvider userAgentHeaderProvider =
new HeaderProvider() {
@Override
public Map<String, String> getHeaders() {
final Map<String, String> headers = new HashMap<>();
headers.put("user-agent", "test-agent");
return headers;
}
};
final SpannerOptions options =
createSpannerOptions().toBuilder().setHeaderProvider(userAgentHeaderProvider).build();
final Spanner spanner = options.getService();
final DatabaseClient databaseClient =
spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"));
try (final ResultSet rs = databaseClient.singleUse().executeQuery(SELECT1AND2)) {
rs.next();
}

assertThat(seenHeaders.get(Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER)))
thiagotnunes marked this conversation as resolved.
Show resolved Hide resolved
.contains("test-agent");
}

@SuppressWarnings("rawtypes")
private SpannerOptions createSpannerOptions() {
String endpoint = address.getHostString() + ":" + server.getPort();
Expand Down