Skip to content

[DRAFT] fix: deduplicate x-goog-api-client headers#17616

Draft
daniel-sanche wants to merge 7 commits into
mainfrom
deduplicate_headers
Draft

[DRAFT] fix: deduplicate x-goog-api-client headers#17616
daniel-sanche wants to merge 7 commits into
mainfrom
deduplicate_headers

Conversation

@daniel-sanche

Copy link
Copy Markdown
Contributor

We currently populate the x-goog-api-client header multiple times, in different places in the stack (api-core, google-aith, user-provided, etc). While some backend systems are able to handle the duplicate headers, it can cause issues for others

This PR explores a potential fix:

  • the CapicCallable wrapper stores this header value separately at init time, and appends the combines if the user passes in a new copy of the header at runtime, instead of passing through multiple copies
  • The google-auth AuthMetadataPlugin class has a new suppress_metrics_header argument. When enabled, it won't append its own x-goog-api-client header, and allow the gapic wrapper to be the only writer (this header should only be required for auth-spefici HTTP calls, not standard gapic rpcs)

b/477429588

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces changes to handle and merge duplicate x-goog-api-client metadata headers in google-api-core and allows suppressing these metrics headers in google-auth's AuthMetadataPlugin. Feedback on the changes includes a recommendation to copy self._arbitrary_metadata to prevent shared state mutation, and a suggestion to simplify the _extract_metrics_header function into a single-pass loop for better readability and compatibility with general iterables.

if self._metrics_values:
kwargs["metadata"] = [(client_info.METRICS_METADATA_KEY, self._metrics_values)] + self._arbitrary_metadata
else:
kwargs["metadata"] = self._arbitrary_metadata

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Assigning self._arbitrary_metadata directly to kwargs["metadata"] can lead to unexpected side effects. Since _GapicCallable is a reusable wrapper called multiple times, any downstream code or transport layer that modifies kwargs["metadata"] in-place will mutate self._arbitrary_metadata for all subsequent calls.

To prevent shared state mutation, please copy the list using list(self._arbitrary_metadata).

Suggested change
kwargs["metadata"] = self._arbitrary_metadata
kwargs["metadata"] = list(self._arbitrary_metadata)

Comment on lines +71 to +86
if not metadata:
return [], []

for i, (key, val) in enumerate(metadata):
if key == client_info.METRICS_METADATA_KEY:
# Key located. Check the rest of the list for duplicate entries
arbitrary_metadata = list(metadata[:i])
metric_values = [val]
for k, v in metadata[i+1:]:
if k == client_info.METRICS_METADATA_KEY:
metric_values.append(v)
else:
arbitrary_metadata.append((k, v))
return arbitrary_metadata, metric_values
# No key found
return list(metadata), []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation of _extract_metrics_header uses index slicing (metadata[:i] and metadata[i+1:]) and multiple loops. This can be simplified into a single-pass loop that is more readable, performs better, and is more robust because it works on any iterable (including generators/iterators) without raising a TypeError on slicing.

    if not metadata:
        return [], []

    arbitrary_metadata = []
    metric_values = []
    for key, val in metadata:
        if key == client_info.METRICS_METADATA_KEY:
            metric_values.append(val)
        else:
            arbitrary_metadata.append((key, val))
    return arbitrary_metadata, metric_values

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant