Skip to content

Commit 6f625b3

Browse files
committed
feat(gax): add getFirstHeader to HttpHeadersUtils
1 parent 3e98a88 commit 6f625b3

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpHeadersUtils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.google.api.client.util.FieldInfo;
3535
import com.google.api.core.InternalApi;
3636
import com.google.common.collect.ImmutableList;
37+
import com.google.common.collect.Iterables;
3738
import java.util.List;
3839
import java.util.Map;
3940
import org.jspecify.annotations.NullMarked;
@@ -52,6 +53,33 @@ public class HttpHeadersUtils {
5253
return null;
5354
}
5455

56+
/**
57+
* Retrieves the first string value of a header by case-insensitive name from a headers map.
58+
*
59+
* @param headers the map of header names to header values
60+
* @param name the case-insensitive header name to look up
61+
* @return the first string value of the header, or {@code null} if not found
62+
*/
63+
public static @Nullable String getFirstHeader(Map<String, Object> headers, String name) {
64+
for (Map.Entry<String, Object> entry : headers.entrySet()) {
65+
if (entry.getKey().equalsIgnoreCase(name)) {
66+
return extractFirstString(entry.getValue());
67+
}
68+
}
69+
return null;
70+
}
71+
72+
private static @Nullable String extractFirstString(@Nullable Object headerValue) {
73+
if (headerValue == null) {
74+
return null;
75+
}
76+
if (headerValue instanceof Iterable) {
77+
Object firstElement = Iterables.getFirst((Iterable<?>) headerValue, null);
78+
return firstElement != null ? firstElement.toString() : null;
79+
}
80+
return headerValue.toString();
81+
}
82+
5583
public static HttpHeaders setHeaders(HttpHeaders headers, Map<String, String> headersMap) {
5684
for (Map.Entry<String, String> entry : headersMap.entrySet()) {
5785
setHeader(headers, entry.getKey(), entry.getValue());

sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpHeadersUtilsTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
*/
3030
package com.google.api.gax.httpjson;
3131

32+
import static com.google.common.truth.Truth.assertThat;
3233
import static org.junit.jupiter.api.Assertions.assertEquals;
3334
import static org.junit.jupiter.api.Assertions.assertNull;
3435

3536
import com.google.api.client.http.HttpHeaders;
3637
import com.google.common.collect.ImmutableList;
3738
import com.google.common.collect.ImmutableMap;
39+
import java.util.Collections;
3840
import java.util.Map;
3941
import org.junit.jupiter.api.Test;
4042

@@ -130,4 +132,68 @@ void testGetUserAgentValue() {
130132
headersMap = ImmutableMap.of("Custom-Header", "CustomHeader", "accept", "Accept");
131133
assertNull(HttpHeadersUtils.getUserAgentValue(headersMap));
132134
}
135+
136+
@Test
137+
void getFirstHeader_exactMatch_returnsValue() {
138+
Map<String, Object> headers =
139+
ImmutableMap.of("X-Goog-Upload-URL", "https://test.googleapis.com/upload");
140+
141+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "X-Goog-Upload-URL"))
142+
.isEqualTo("https://test.googleapis.com/upload");
143+
}
144+
145+
@Test
146+
void getFirstHeader_caseInsensitiveMatch_returnsValue() {
147+
Map<String, Object> headers =
148+
ImmutableMap.of("X-Goog-Upload-URL", "https://test.googleapis.com/upload");
149+
150+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "x-goog-upload-url"))
151+
.isEqualTo("https://test.googleapis.com/upload");
152+
}
153+
154+
@Test
155+
void getFirstHeader_iterableValue_returnsFirstElement() {
156+
Map<String, Object> headers =
157+
ImmutableMap.of(
158+
"Location", ImmutableList.of("https://redirect.url", "https://secondary.url"));
159+
160+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "location"))
161+
.isEqualTo("https://redirect.url");
162+
}
163+
164+
@Test
165+
void getFirstHeader_nonStringValue_convertsToString() {
166+
Map<String, Object> headers = ImmutableMap.of("Content-Length", 1024L);
167+
168+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "content-length")).isEqualTo("1024");
169+
}
170+
171+
@Test
172+
void getFirstHeader_emptyIterable_returnsNull() {
173+
Map<String, Object> headers = ImmutableMap.of("Empty-List-Header", ImmutableList.of());
174+
175+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "empty-list-header")).isNull();
176+
}
177+
178+
@Test
179+
void getFirstHeader_nullFirstElementInIterable_returnsNull() {
180+
Map<String, Object> headers =
181+
Collections.singletonMap("Null-First-Header", Collections.singletonList(null));
182+
183+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "null-first-header")).isNull();
184+
}
185+
186+
@Test
187+
void getFirstHeader_nullValueInMap_returnsNull() {
188+
Map<String, Object> headers = Collections.singletonMap("Null-Value-Header", null);
189+
190+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "null-value-header")).isNull();
191+
}
192+
193+
@Test
194+
void getFirstHeader_headerNotFound_returnsNull() {
195+
Map<String, Object> headers = ImmutableMap.of("Existing-Header", "value");
196+
197+
assertThat(HttpHeadersUtils.getFirstHeader(headers, "non-existent-header")).isNull();
198+
}
133199
}

0 commit comments

Comments
 (0)