Skip to content

Commit 7658d02

Browse files
committed
attempt deduplicating x-goog-api-client headers
1 parent 58912bc commit 7658d02

4 files changed

Lines changed: 65 additions & 14 deletions

File tree

packages/google-api-core/google/api_core/gapic_v1/method.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,25 @@ def __call__(
114114

115115
# Add the user agent metadata to the call.
116116
if self._metadata is not None:
117-
metadata = kwargs.get("metadata", [])
118-
# Due to the nature of invocation, None should be treated the same
119-
# as not specified.
120-
if metadata is None:
121-
metadata = []
122-
metadata = list(metadata)
123-
metadata.extend(self._metadata)
124-
kwargs["metadata"] = metadata
117+
metadata = kwargs.get("metadata")
118+
if not metadata:
119+
kwargs["metadata"] = self._metadata
120+
else:
121+
# Merge user-supplied metadata with library-supplied metadata.
122+
# All keys in gRPC metadata are already lowercase.
123+
from itertools import chain
124+
metadata = list(metadata)
125+
api_client_values = []
126+
merged_metadata = []
127+
for key, val in chain(metadata, self._metadata):
128+
if key == "x-goog-api-client":
129+
api_client_values.append(val)
130+
else:
131+
merged_metadata.append((key, val))
132+
if api_client_values:
133+
merged_metadata.append(("x-goog-api-client", " ".join(api_client_values)))
134+
kwargs["metadata"] = merged_metadata
135+
125136
if self._compression is not None:
126137
kwargs["compression"] = compression
127138

packages/google-api-core/google/api_core/grpc_helpers.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,20 @@ def _create_composite_credentials(
254254
request = google.auth.transport.requests.Request()
255255

256256
# Create the metadata plugin for inserting the authorization header.
257-
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
258-
credentials,
259-
request,
260-
default_host=default_host,
261-
)
257+
try:
258+
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
259+
credentials,
260+
request,
261+
default_host=default_host,
262+
suppress_metrics_header=True,
263+
)
264+
except TypeError:
265+
# Support older versions of google-auth that do not accept suppress_metrics_header
266+
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
267+
credentials,
268+
request,
269+
default_host=default_host,
270+
)
262271

263272
# Create a set of grpc.CallCredentials using the metadata plugin.
264273
google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin)

packages/google-auth/google/auth/transport/grpc.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ class AuthMetadataPlugin(grpc.AuthMetadataPlugin):
4949
account credentials.
5050
"""
5151

52-
def __init__(self, credentials, request, default_host=None):
52+
def __init__(self, credentials, request, default_host=None, suppress_metrics_header=False):
5353
# pylint: disable=no-value-for-parameter
5454
# pylint doesn't realize that the super method takes no arguments
5555
# because this class is the same name as the superclass.
5656
super(AuthMetadataPlugin, self).__init__()
5757
self._credentials = credentials
5858
self._request = request
5959
self._default_host = default_host
60+
self._suppress_metrics_header = suppress_metrics_header
6061

6162
def _get_authorization_headers(self, context):
6263
"""Gets the authorization headers for a request.
@@ -80,6 +81,9 @@ def _get_authorization_headers(self, context):
8081
self._request, context.method_name, context.service_url, headers
8182
)
8283

84+
if self._suppress_metrics_header and "x-goog-api-client" in headers:
85+
del headers["x-goog-api-client"]
86+
8387
return list(headers.items())
8488

8589
def __call__(self, context, callback):

packages/google-auth/tests/transport/test_grpc.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,33 @@ def test__get_authorization_headers_with_service_account_and_default_host(self):
132132
"https://{}/".format(default_host)
133133
)
134134

135+
def test_suppress_metrics_header(self):
136+
credentials = mock.create_autospec(service_account.Credentials)
137+
# Mock credentials before_request that adds metric and authorization
138+
def mock_before_request(request, method, url, headers):
139+
headers["x-goog-api-client"] = "foo"
140+
headers["authorization"] = "Bearer token"
141+
credentials.before_request.side_effect = mock_before_request
142+
request = mock.create_autospec(transport.Request)
143+
144+
# By default, suppress_metrics_header=False
145+
plugin = google.auth.transport.grpc.AuthMetadataPlugin(credentials, request)
146+
context = mock.create_autospec(grpc.AuthMetadataContext, instance=True)
147+
context.method_name = "methodName"
148+
context.service_url = "https://pubsub.googleapis.com/methodName"
149+
150+
headers = dict(plugin._get_authorization_headers(context))
151+
assert "x-goog-api-client" in headers
152+
assert headers["x-goog-api-client"] == "foo"
153+
154+
# With suppress_metrics_header=True
155+
plugin_suppressed = google.auth.transport.grpc.AuthMetadataPlugin(
156+
credentials, request, suppress_metrics_header=True
157+
)
158+
headers_suppressed = dict(plugin_suppressed._get_authorization_headers(context))
159+
assert "x-goog-api-client" not in headers_suppressed
160+
assert headers_suppressed["authorization"] == "Bearer token"
161+
135162

136163
@mock.patch(
137164
"google.auth.transport._mtls_helper.get_client_ssl_credentials", autospec=True

0 commit comments

Comments
 (0)