Skip to content

feat(gax): add getSingleHeader to HttpHeadersUtils - #14137

Merged
whowes merged 1 commit into
mainfrom
whowes/get-first-header
Aug 28, 2026
Merged

feat(gax): add getSingleHeader to HttpHeadersUtils#14137
whowes merged 1 commit into
mainfrom
whowes/get-first-header

Conversation

@whowes

@whowes whowes commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

The GAX HTTP/JSON stack stores response headers in a Map<String, Object> where values may be Strings, numbers, Iterables, etc., and header names may vary in casing depending on the HTTP version. This method provides case-insensitive lookup and safely extracts a single value in a typesafe way when it exists, returning null if not and throwing an exception if multiple values exist.

This will be used in resumable upload support to retrieve protocol header values (e.g. X-Goog-Upload-URL, Location, and X-Goog-Upload-Status).

@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 a new utility method getFirstHeader in HttpHeadersUtils to retrieve the first string value of a header by case-insensitive name from a headers map, along with a helper method extractFirstString to handle different header value types (such as iterables and non-string objects). Comprehensive unit tests have also been added to cover various scenarios, including case-insensitivity, iterable values, null values, and non-existent headers. There are no review comments, and I have no feedback to provide.

@whowes whowes changed the title whowes/get first header feat(gax): add getFirstHeader to HttpHeadersUtils Aug 19, 2026
@whowes
whowes force-pushed the whowes/get-first-header branch 2 times, most recently from d1c96b2 to b7b5130 Compare August 21, 2026 00:37
@whowes
whowes force-pushed the whowes/get-first-header branch from b7b5130 to ed5f3b7 Compare August 24, 2026 19:40
return null;
}

private static @Nullable String extractFirstString(@Nullable Object headerValue) {

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.

Does the protocol specify that we always use the first value of the header value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe that the protocol's control responses are only single-value headers, so I don't think that's part of the specification.

Since the value in the headers map can be a list (I'm not super familiar with this quirk of the HTTP client myself but it's the same pattern as documented in this comment) I opted to only return the first header. I suppose to be more general we could instead have the utility here return all values and have downstream resumable upload code handle the multi-value case.

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.

If the protocol says single-value headers only, can we change the code to only support that?
If the backend does return a list of values in the future though, we may need to adjust the logic accordingly. I don't think always getting the first value is a good assumption to make at this moment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Switched this to getSingleHeader() instead that fails-fast if a multi-valued header is encountered.

@whowes
whowes force-pushed the whowes/get-first-header branch from ed5f3b7 to 8f97deb Compare August 25, 2026 16:34
@whowes
whowes force-pushed the whowes/get-first-header branch from 8f97deb to 04857e5 Compare August 25, 2026 21:44
@whowes
whowes force-pushed the whowes/get-first-header branch from 04857e5 to 6c9c801 Compare August 25, 2026 22:00
@whowes
whowes force-pushed the whowes/get-first-header branch from 6c9c801 to 5703db2 Compare August 26, 2026 17:56
@whowes
whowes force-pushed the whowes/get-first-header branch 2 times, most recently from e31fa27 to 9402bac Compare August 26, 2026 18:28
@whowes
whowes force-pushed the whowes/get-first-header branch from 9402bac to e9ec582 Compare August 26, 2026 18:38
@whowes
whowes force-pushed the whowes/get-first-header branch from e9ec582 to 545bd73 Compare August 26, 2026 20:40
@whowes
whowes force-pushed the whowes/get-first-header branch from 545bd73 to 0b2a27e Compare August 26, 2026 20:53
@whowes
whowes force-pushed the whowes/get-first-header branch from 0b2a27e to 36fcba2 Compare August 27, 2026 00:42
@whowes
whowes force-pushed the whowes/get-first-header branch from 36fcba2 to 0a47cb0 Compare August 27, 2026 00:42
@whowes whowes changed the title feat(gax): add getFirstHeader to HttpHeadersUtils feat(gax): add getSingleHeader to HttpHeadersUtils Aug 27, 2026
* @throws IllegalArgumentException if multiple values are present for the header
*/
public static @Nullable String getSingleHeader(Map<String, Object> headers, String name) {
for (Map.Entry<String, Object> entry : headers.entrySet()) {

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.

nit: We can use stream and findFirst for better readability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great suggestion; done.

@whowes
whowes force-pushed the whowes/get-first-header branch from 0a47cb0 to c92b3d3 Compare August 27, 2026 15:27
@whowes
whowes force-pushed the whowes/get-first-header branch from c92b3d3 to 4e378cb Compare August 27, 2026 20:15
@whowes
whowes marked this pull request as ready for review August 27, 2026 20:19
@whowes
whowes requested review from a team as code owners August 27, 2026 20:19
@whowes
whowes force-pushed the whowes/get-first-header branch from 4e378cb to 4d1c16f Compare August 27, 2026 20:58
@whowes
whowes force-pushed the whowes/get-first-header branch from 4d1c16f to 11f54da Compare August 27, 2026 23:52
@whowes
whowes force-pushed the whowes/get-first-header branch from 11f54da to 2fca609 Compare August 28, 2026 01:34
@whowes
whowes force-pushed the whowes/get-first-header branch 3 times, most recently from a53b52e to 4301616 Compare August 28, 2026 17:23
@whowes
whowes force-pushed the whowes/get-first-header branch from 4301616 to e18b036 Compare August 28, 2026 19:11
Base automatically changed from whowes/start-unary-call to main August 28, 2026 20:51
@whowes
whowes force-pushed the whowes/get-first-header branch from e18b036 to 3922c3b Compare August 28, 2026 20:51
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@whowes
whowes merged commit f114422 into main Aug 28, 2026
302 of 306 checks passed
@whowes
whowes deleted the whowes/get-first-header branch August 28, 2026 21:40
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.

2 participants