Skip to content

Commit f355dc4

Browse files
authored
[Java][Native] Fix request compression (#22688)
* fix request compression * fix edge case * regenerated samples
1 parent 73c9c1c commit f355dc4

File tree

8 files changed

+131
-8
lines changed

8 files changed

+131
-8
lines changed

bin/configs/java-native-useGzipFeature.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ additionalProperties:
77
artifactId: petstore-native-useGzipFeature
88
hideGenerationTimestamp: "true"
99
useJakartaEe: "true"
10+
useGzipFeature: "true"

modules/openapi-generator/src/main/resources/Java/libraries/native/ApiClient.mustache

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ public class ApiClient {
489489
if (encoding.isPresent()) {
490490
for (String token : encoding.get().split(",")) {
491491
if ("gzip".equalsIgnoreCase(token.trim())) {
492-
return new GZIPInputStream(body);
492+
return new GZIPInputStream(body, 8192);
493493
}
494494
}
495495
}
@@ -531,9 +531,9 @@ public class ApiClient {
531531
}
532532

533533
private boolean fillBuffer() throws IOException {
534-
ensureInitialized();
535534
while (chunkPosition >= currentChunk.length) {
536535
buffer.reset();
536+
ensureInitialized();
537537
if (finished) {
538538
return false;
539539
}
@@ -570,6 +570,9 @@ public class ApiClient {
570570

571571
@Override
572572
public int read(byte[] b, int off, int len) throws IOException {
573+
if (len == 0) {
574+
return 0;
575+
}
573576
if (!fillBuffer()) {
574577
return -1;
575578
}

samples/client/echo_api/java/native/src/main/java/org/openapitools/client/ApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
478478
if (encoding.isPresent()) {
479479
for (String token : encoding.get().split(",")) {
480480
if ("gzip".equalsIgnoreCase(token.trim())) {
481-
return new GZIPInputStream(body);
481+
return new GZIPInputStream(body, 8192);
482482
}
483483
}
484484
}

samples/client/petstore/java/native-async/src/main/java/org/openapitools/client/ApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
478478
if (encoding.isPresent()) {
479479
for (String token : encoding.get().split(",")) {
480480
if ("gzip".equalsIgnoreCase(token.trim())) {
481-
return new GZIPInputStream(body);
481+
return new GZIPInputStream(body, 8192);
482482
}
483483
}
484484
}

samples/client/petstore/java/native-jakarta/src/main/java/org/openapitools/client/ApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
478478
if (encoding.isPresent()) {
479479
for (String token : encoding.get().split(",")) {
480480
if ("gzip".equalsIgnoreCase(token.trim())) {
481-
return new GZIPInputStream(body);
481+
return new GZIPInputStream(body, 8192);
482482
}
483483
}
484484
}

samples/client/petstore/java/native-useGzipFeature/src/main/java/org/openapitools/client/ApiClient.java

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.io.InputStream;
2323
import java.io.IOException;
24+
import java.io.ByteArrayOutputStream;
2425
import java.net.URI;
2526
import java.net.URLEncoder;
2627
import java.net.http.HttpClient;
@@ -37,6 +38,9 @@
3738
import java.util.function.Consumer;
3839
import java.util.Optional;
3940
import java.util.zip.GZIPInputStream;
41+
import java.util.function.Supplier;
42+
import java.util.Objects;
43+
import java.util.zip.GZIPOutputStream;
4044
import java.util.stream.Collectors;
4145

4246
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -478,11 +482,122 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
478482
if (encoding.isPresent()) {
479483
for (String token : encoding.get().split(",")) {
480484
if ("gzip".equalsIgnoreCase(token.trim())) {
481-
return new GZIPInputStream(body);
485+
return new GZIPInputStream(body, 8192);
482486
}
483487
}
484488
}
485489
return body;
486490
}
487491

492+
/**
493+
* Wraps a request body supplier with a streaming GZIP compressor so large payloads
494+
* can be sent without buffering the entire contents in memory.
495+
*
496+
* @param bodySupplier Supplies the original request body InputStream
497+
* @return BodyPublisher that emits gzip-compressed bytes from the supplied stream
498+
*/
499+
public static HttpRequest.BodyPublisher gzipRequestBody(Supplier<InputStream> bodySupplier) {
500+
Objects.requireNonNull(bodySupplier, "bodySupplier must not be null");
501+
return HttpRequest.BodyPublishers.ofInputStream(() -> new GzipCompressingInputStream(bodySupplier));
502+
}
503+
504+
private static final class GzipCompressingInputStream extends InputStream {
505+
private final Supplier<InputStream> supplier;
506+
private final byte[] readBuffer = new byte[8192];
507+
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
508+
private InputStream source;
509+
private GZIPOutputStream gzipStream;
510+
private byte[] currentChunk = new byte[0];
511+
private int chunkPosition = 0;
512+
private boolean finished = false;
513+
514+
private GzipCompressingInputStream(Supplier<InputStream> supplier) {
515+
this.supplier = Objects.requireNonNull(supplier, "bodySupplier must not be null");
516+
}
517+
518+
private void ensureInitialized() throws IOException {
519+
if (source == null) {
520+
source = Objects.requireNonNull(supplier.get(), "bodySupplier returned null InputStream");
521+
gzipStream = new GZIPOutputStream(buffer, true);
522+
}
523+
}
524+
525+
private boolean fillBuffer() throws IOException {
526+
while (chunkPosition >= currentChunk.length) {
527+
buffer.reset();
528+
ensureInitialized();
529+
if (finished) {
530+
return false;
531+
}
532+
int bytesRead = source.read(readBuffer);
533+
if (bytesRead == -1) {
534+
gzipStream.finish();
535+
gzipStream.close();
536+
source.close();
537+
finished = true;
538+
} else {
539+
gzipStream.write(readBuffer, 0, bytesRead);
540+
gzipStream.flush();
541+
}
542+
currentChunk = buffer.toByteArray();
543+
chunkPosition = 0;
544+
if (currentChunk.length == 0 && !finished) {
545+
continue;
546+
}
547+
if (currentChunk.length == 0 && finished) {
548+
return false;
549+
}
550+
return true;
551+
}
552+
return true;
553+
}
554+
555+
@Override
556+
public int read() throws IOException {
557+
if (!fillBuffer()) {
558+
return -1;
559+
}
560+
return currentChunk[chunkPosition++] & 0xFF;
561+
}
562+
563+
@Override
564+
public int read(byte[] b, int off, int len) throws IOException {
565+
if (len == 0) {
566+
return 0;
567+
}
568+
if (!fillBuffer()) {
569+
return -1;
570+
}
571+
int bytesToCopy = Math.min(len, currentChunk.length - chunkPosition);
572+
System.arraycopy(currentChunk, chunkPosition, b, off, bytesToCopy);
573+
chunkPosition += bytesToCopy;
574+
return bytesToCopy;
575+
}
576+
577+
@Override
578+
public void close() throws IOException {
579+
IOException exception = null;
580+
if (source != null) {
581+
try {
582+
source.close();
583+
} catch (IOException e) {
584+
exception = e;
585+
} finally {
586+
source = null;
587+
}
588+
}
589+
if (gzipStream != null) {
590+
try {
591+
gzipStream.close();
592+
} catch (IOException e) {
593+
exception = exception == null ? e : exception;
594+
} finally {
595+
gzipStream = null;
596+
}
597+
}
598+
if (exception != null) {
599+
throw exception;
600+
}
601+
}
602+
}
488603
}

samples/client/petstore/java/native-useGzipFeature/src/main/java/org/openapitools/client/api/PetApi.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.Map;
4545
import java.util.Set;
4646
import java.util.function.Consumer;
47+
import java.util.function.Supplier;
4748

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

268269
localVarRequestBuilder.header("Content-Type", "application/json");
269270
localVarRequestBuilder.header("Accept", "application/xml, application/json");
271+
localVarRequestBuilder.header("Accept-Encoding", "gzip");
270272

271273
try {
272274
byte[] localVarPostBody = memberVarObjectMapper.writeValueAsBytes(pet);
273-
localVarRequestBuilder.method("POST", HttpRequest.BodyPublishers.ofByteArray(localVarPostBody));
275+
Supplier<InputStream> localVarRequestBodySupplier = () -> new ByteArrayInputStream(localVarPostBody);
276+
localVarRequestBuilder.header("Content-Encoding", "gzip");
277+
localVarRequestBuilder.method("POST", ApiClient.gzipRequestBody(localVarRequestBodySupplier));
274278
} catch (IOException e) {
275279
throw new ApiException(e);
276280
}

samples/client/petstore/java/native/src/main/java/org/openapitools/client/ApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public static InputStream getResponseBody(HttpResponse<InputStream> response) th
478478
if (encoding.isPresent()) {
479479
for (String token : encoding.get().split(",")) {
480480
if ("gzip".equalsIgnoreCase(token.trim())) {
481-
return new GZIPInputStream(body);
481+
return new GZIPInputStream(body, 8192);
482482
}
483483
}
484484
}

0 commit comments

Comments
 (0)