feat(gax): add HTTP/JSON implementation of ResumableUploadClient.startUpload - #14091
feat(gax): add HTTP/JSON implementation of ResumableUploadClient.startUpload#14091whowes wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new low-level resumable upload client framework for HTTP/JSON transport, including the ResumableUploadClient interface, ResumableUploadSession metadata, and StartUploadRequest parameters, along with their corresponding unit tests. Feedback on the implementation suggests simplifying the response header parsing logic in HttpJsonResumableUploadClient by removing an unnecessary instanceof HttpHeaders check that is likely always false.
| HttpHeaders headers; | ||
| if (responseHeaders.getHeaders() instanceof HttpHeaders) { | ||
| headers = (HttpHeaders) responseHeaders.getHeaders(); | ||
| } else { | ||
| headers = new HttpHeaders(); | ||
| headers.putAll(responseHeaders.getHeaders()); | ||
| } |
There was a problem hiding this comment.
The instanceof HttpHeaders check is likely always false, as HttpJsonMetadata.getHeaders() returns a Map<String, List<String>> which is usually not an HttpHeaders instance at runtime. This logic can be simplified by removing the conditional and always creating a new HttpHeaders object. This makes the code cleaner and removes a potentially dead code path.
HttpHeaders headers = new HttpHeaders();
headers.putAll(responseHeaders.getHeaders());16d875f to
e808397
Compare
e808397 to
dbd35b6
Compare
dbd35b6 to
1435243
Compare
1435243 to
880655e
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the HttpJsonResumableUploadClient class and its associated tests to support resumable uploads over HTTP/JSON. The review feedback suggests optimizing the onHeaders method by replacing the heavy HttpHeaders class with a direct, case-insensitive lookup on the raw headers map to avoid reflection and unnecessary allocations. Consequently, the unused import for HttpHeaders should also be removed.
| */ | ||
| package com.google.api.gax.httpjson; | ||
|
|
||
| import com.google.api.client.http.HttpHeaders; |
| @Override | ||
| public void onHeaders(HttpJsonMetadata responseHeaders) { | ||
| if (responseHeaders != null && responseHeaders.getHeaders() != null) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.putAll(responseHeaders.getHeaders()); | ||
|
|
||
| String url = headers.getFirstHeaderStringValue(UPLOAD_URL_HEADER); | ||
| if (Strings.isNullOrEmpty(url)) { | ||
| url = headers.getLocation(); | ||
| } | ||
| if (!Strings.isNullOrEmpty(url)) { | ||
| this.uploadUrl = url; | ||
| } | ||
|
|
||
| String granularityStr = headers.getFirstHeaderStringValue(UPLOAD_GRANULARITY_HEADER); | ||
| if (!Strings.isNullOrEmpty(granularityStr)) { | ||
| try { | ||
| this.chunkGranularity = Long.parseLong(granularityStr); | ||
| } catch (NumberFormatException ignored) { | ||
| this.chunkGranularity = 1L; | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Using com.google.api.client.http.HttpHeaders and calling putAll with all response headers is highly inefficient. HttpHeaders is a heavy class that uses reflection to map header keys to class fields. Since we only need to extract a couple of specific headers case-insensitively, we can perform a direct case-insensitive lookup on the raw headers map. This avoids unnecessary object allocation and reflection overhead.
@Override
public void onHeaders(HttpJsonMetadata responseHeaders) {
if (responseHeaders != null && responseHeaders.getHeaders() != null) {
Map<String, List<String>> headers = responseHeaders.getHeaders();
String url = getFirstHeader(headers, UPLOAD_URL_HEADER);
if (Strings.isNullOrEmpty(url)) {
url = getFirstHeader(headers, "Location");
}
if (!Strings.isNullOrEmpty(url)) {
this.uploadUrl = url;
}
String granularityStr = getFirstHeader(headers, UPLOAD_GRANULARITY_HEADER);
if (!Strings.isNullOrEmpty(granularityStr)) {
try {
this.chunkGranularity = Long.parseLong(granularityStr);
} catch (NumberFormatException ignored) {
this.chunkGranularity = 1L;
}
}
}
}
@Nullable
private static String getFirstHeader(Map<String, List<String>> headers, String name) {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
if (entry.getKey().equalsIgnoreCase(name)) {
List<String> values = entry.getValue();
return values != null && !values.isEmpty() ? values.get(0) : null;
}
}
return null;
}References
- When annotating a method with
@Nullable, verify if any callers pass the returned value directly to APIs that do not accept nulls (such as Guava'sImmutableMap.Builder). If null checks are missing, they should be added or tracked for follow-up work.
880655e to
2ad83c8
Compare
2ad83c8 to
2937dba
Compare
|
|





Stack created with GitHub Stacks CLI • Give Feedback 💬