Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/configs/java-native-useGzipFeature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ additionalProperties:
artifactId: petstore-native-useGzipFeature
hideGenerationTimestamp: "true"
useJakartaEe: "true"
useGzipFeature: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public class ApiClient {
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
Expand Down Expand Up @@ -531,9 +531,9 @@ public class ApiClient {
}

private boolean fillBuffer() throws IOException {
ensureInitialized();
while (chunkPosition >= currentChunk.length) {
buffer.reset();
ensureInitialized();
if (finished) {
return false;
}
Expand Down Expand Up @@ -570,6 +570,9 @@ public class ApiClient {

@Override
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
Comment on lines +573 to +575
Copy link

Choose a reason for hiding this comment

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

P2: read(byte[],off,len) now returns 0 for len==0 without null/bounds validation, diverging from InputStream contract and masking invalid arguments

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/Java/libraries/native/ApiClient.mustache, line 573:

<comment>read(byte[],off,len) now returns 0 for len==0 without null/bounds validation, diverging from InputStream contract and masking invalid arguments</comment>

<file context>
@@ -570,6 +570,9 @@ public class ApiClient {
 
     @Override
     public int read(byte[] b, int off, int len) throws IOException {
+      if (len == 0) {
+        return 0;
+      }
</file context>
Suggested change
if (len == 0) {
return 0;
}
Objects.requireNonNull(b, "buffer must not be null");
if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, I have fixed this edge-case

if (!fillBuffer()) {
return -1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.io.InputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
Expand All @@ -37,6 +38,9 @@
import java.util.function.Consumer;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
import java.util.function.Supplier;
import java.util.Objects;
import java.util.zip.GZIPOutputStream;
import java.util.stream.Collectors;

import static java.nio.charset.StandardCharsets.UTF_8;
Expand Down Expand Up @@ -478,11 +482,122 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
return body;
}

/**
* Wraps a request body supplier with a streaming GZIP compressor so large payloads
* can be sent without buffering the entire contents in memory.
*
* @param bodySupplier Supplies the original request body InputStream
* @return BodyPublisher that emits gzip-compressed bytes from the supplied stream
*/
public static HttpRequest.BodyPublisher gzipRequestBody(Supplier<InputStream> bodySupplier) {
Objects.requireNonNull(bodySupplier, "bodySupplier must not be null");
return HttpRequest.BodyPublishers.ofInputStream(() -> new GzipCompressingInputStream(bodySupplier));
}

private static final class GzipCompressingInputStream extends InputStream {
private final Supplier<InputStream> supplier;
private final byte[] readBuffer = new byte[8192];
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private InputStream source;
private GZIPOutputStream gzipStream;
private byte[] currentChunk = new byte[0];
private int chunkPosition = 0;
private boolean finished = false;

private GzipCompressingInputStream(Supplier<InputStream> supplier) {
this.supplier = Objects.requireNonNull(supplier, "bodySupplier must not be null");
}

private void ensureInitialized() throws IOException {
if (source == null) {
source = Objects.requireNonNull(supplier.get(), "bodySupplier returned null InputStream");
gzipStream = new GZIPOutputStream(buffer, true);
}
}

private boolean fillBuffer() throws IOException {
while (chunkPosition >= currentChunk.length) {
buffer.reset();
ensureInitialized();
if (finished) {
return false;
}
int bytesRead = source.read(readBuffer);
if (bytesRead == -1) {
gzipStream.finish();
gzipStream.close();
source.close();
finished = true;
} else {
gzipStream.write(readBuffer, 0, bytesRead);
gzipStream.flush();
}
currentChunk = buffer.toByteArray();
chunkPosition = 0;
if (currentChunk.length == 0 && !finished) {
continue;
}
if (currentChunk.length == 0 && finished) {
return false;
}
return true;
}
return true;
}

@Override
public int read() throws IOException {
if (!fillBuffer()) {
return -1;
}
return currentChunk[chunkPosition++] & 0xFF;
}

@Override
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
if (!fillBuffer()) {
return -1;
}
int bytesToCopy = Math.min(len, currentChunk.length - chunkPosition);
System.arraycopy(currentChunk, chunkPosition, b, off, bytesToCopy);
chunkPosition += bytesToCopy;
return bytesToCopy;
}

@Override
public void close() throws IOException {
IOException exception = null;
if (source != null) {
try {
source.close();
} catch (IOException e) {
exception = e;
} finally {
source = null;
}
}
if (gzipStream != null) {
try {
gzipStream.close();
} catch (IOException e) {
exception = exception == null ? e : exception;
} finally {
gzipStream = null;
}
}
if (exception != null) {
throw exception;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.19.0-SNAPSHOT")
public class PetApi {
Expand Down Expand Up @@ -267,10 +268,13 @@ private HttpRequest.Builder addPetRequestBuilder(@jakarta.annotation.Nonnull Pet

localVarRequestBuilder.header("Content-Type", "application/json");
localVarRequestBuilder.header("Accept", "application/xml, application/json");
localVarRequestBuilder.header("Accept-Encoding", "gzip");

try {
byte[] localVarPostBody = memberVarObjectMapper.writeValueAsBytes(pet);
localVarRequestBuilder.method("POST", HttpRequest.BodyPublishers.ofByteArray(localVarPostBody));
Supplier<InputStream> localVarRequestBodySupplier = () -> new ByteArrayInputStream(localVarPostBody);
localVarRequestBuilder.header("Content-Encoding", "gzip");
localVarRequestBuilder.method("POST", ApiClient.gzipRequestBody(localVarRequestBodySupplier));
} catch (IOException e) {
throw new ApiException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
if (encoding.isPresent()) {
for (String token : encoding.get().split(",")) {
if ("gzip".equalsIgnoreCase(token.trim())) {
return new GZIPInputStream(body);
return new GZIPInputStream(body, 8192);
}
}
}
Expand Down
Loading