From 2bb7626202633222a4ac2668f2bd23ffad17a9b4 Mon Sep 17 00:00:00 2001 From: Michael Kourlas Date: Wed, 8 Mar 2017 06:00:50 -0500 Subject: [PATCH] [Java] Fix for issue #3365: Support for gzip compression (#4967) * Fix for issue 3365 for Java * Added tests * Convert CRLF to LF * Convert CRLF to LF in tests --- .../codegen/languages/JavaClientCodegen.java | 20 ++++- .../main/resources/Java/ApiClient.mustache | 2 + .../libraries/okhttp-gson/ApiClient.mustache | 5 ++ .../GzipRequestInterceptor.mustache | 70 ++++++++++++++++ .../options/JavaClientOptionsProvider.java | 1 + .../java/io/swagger/client/ApiClient.java | 2 + .../java/io/swagger/client/ApiClient.java | 1 + .../client/GzipRequestInterceptor.java | 81 +++++++++++++++++++ 8 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/GzipRequestInterceptor.mustache create mode 100644 samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/GzipRequestInterceptor.java diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index 1285391cef57..b9152047f387 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java @@ -2,6 +2,7 @@ package io.swagger.codegen.languages; import io.swagger.codegen.*; import io.swagger.codegen.languages.features.BeanValidationFeatures; +import io.swagger.codegen.languages.features.GzipFeatures; import io.swagger.codegen.languages.features.PerformBeanValidationFeatures; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -13,7 +14,9 @@ import java.util.*; import java.util.regex.Pattern; public class JavaClientCodegen extends AbstractJavaCodegen - implements BeanValidationFeatures, PerformBeanValidationFeatures { + implements BeanValidationFeatures, PerformBeanValidationFeatures, + GzipFeatures +{ static final String MEDIA_TYPE = "mediaType"; @SuppressWarnings("hiding") @@ -36,6 +39,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen protected boolean parcelableModel = false; protected boolean useBeanValidation = false; protected boolean performBeanValidation = false; + protected boolean useGzipFeature = false; public JavaClientCodegen() { super(); @@ -53,11 +57,12 @@ public class JavaClientCodegen extends AbstractJavaCodegen cliOptions.add(CliOption.newBoolean(SUPPORT_JAVA6, "Whether to support Java6 with the Jersey1 library.")); cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations")); cliOptions.add(CliOption.newBoolean(PERFORM_BEANVALIDATION, "Perform BeanValidation")); + cliOptions.add(CliOption.newBoolean(USE_GZIP_FEATURE, "Send gzip-encoded requests")); - supportedLibraries.put("jersey1", "HTTP client: Jersey client 1.19.1. JSON processing: Jackson 2.7.0. Enable Java6 support using '-DsupportJava6=true'."); + supportedLibraries.put("jersey1", "HTTP client: Jersey client 1.19.1. JSON processing: Jackson 2.7.0. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'."); supportedLibraries.put("feign", "HTTP client: Netflix Feign 8.16.0. JSON processing: Jackson 2.7.0"); supportedLibraries.put("jersey2", "HTTP client: Jersey client 2.22.2. JSON processing: Jackson 2.7.0"); - supportedLibraries.put("okhttp-gson", "HTTP client: OkHttp 2.7.5. JSON processing: Gson 2.6.2. Enable Parcelable modles on Android using '-DparcelableModel=true'"); + supportedLibraries.put("okhttp-gson", "HTTP client: OkHttp 2.7.5. JSON processing: Gson 2.6.2. Enable Parcelable modles on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'."); supportedLibraries.put(RETROFIT_1, "HTTP client: OkHttp 2.7.5. JSON processing: Gson 2.3.1 (Retrofit 1.9.0). IMPORTANT NOTE: retrofit1.x is no longer actively maintained so please upgrade to 'retrofit2' instead."); supportedLibraries.put(RETROFIT_2, "HTTP client: OkHttp 3.2.0. JSON processing: Gson 2.6.1 (Retrofit 2.0.2). Enable the RxJava adapter using '-DuseRxJava[2]=true'. (RxJava 1.x or 2.x)"); @@ -119,6 +124,10 @@ public class JavaClientCodegen extends AbstractJavaCodegen this.setPerformBeanValidation(convertPropertyToBooleanAndWriteBack(PERFORM_BEANVALIDATION)); } + if (additionalProperties.containsKey(USE_GZIP_FEATURE)) { + this.setUseGzipFeature(convertPropertyToBooleanAndWriteBack(USE_GZIP_FEATURE)); + } + final String invokerFolder = (sourceFolder + '/' + invokerPackage).replace(".", "/"); final String authFolder = (sourceFolder + '/' + invokerPackage + ".auth").replace(".", "/"); @@ -174,6 +183,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); supportingFiles.add(new SupportingFile("ProgressRequestBody.mustache", invokerFolder, "ProgressRequestBody.java")); supportingFiles.add(new SupportingFile("ProgressResponseBody.mustache", invokerFolder, "ProgressResponseBody.java")); + supportingFiles.add(new SupportingFile("GzipRequestInterceptor.mustache", invokerFolder, "GzipRequestInterceptor.java")); additionalProperties.put("gson", "true"); } else if (usesAnyRetrofitLibrary()) { supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.mustache", authFolder, "OAuthOkHttpClient.java")); @@ -377,6 +387,10 @@ public class JavaClientCodegen extends AbstractJavaCodegen this.performBeanValidation = performBeanValidation; } + public void setUseGzipFeature(boolean useGzipFeature) { + this.useGzipFeature = useGzipFeature; + } + final private static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application\\/json(;.*)?"); final private static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application\\/vnd.(.*)+json(;.*)?"); diff --git a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache index 14a215eac4a4..dac15cd9f835 100644 --- a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache @@ -15,6 +15,7 @@ import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.client.filter.GZIPContentEncodingFilter; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.api.client.WebResource.Builder; @@ -110,6 +111,7 @@ public class ApiClient { DefaultClientConfig conf = new DefaultClientConfig(); conf.getSingletons().add(jsonProvider); Client client = Client.create(conf); + client.addFilter(new GZIPContentEncodingFilter({{#useGzipFeature}}true{{/useGzipFeature}}{{^useGzipFeature}}false{{/useGzipFeature}})); if (debugging) { client.addFilter(new LoggingFilter()); } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache index 9c75d6716077..a3f73d0b27b7 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache @@ -128,6 +128,11 @@ public class ApiClient { public ApiClient() { httpClient = new OkHttpClient(); + {{#useGzipFeature}} + // Enable gzip request compression + httpClient.interceptors().add(new GzipRequestInterceptor()); + {{/useGzipFeature}} + verifyingSsl = true; json = new JSON(this); diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/GzipRequestInterceptor.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/GzipRequestInterceptor.mustache new file mode 100644 index 000000000000..23224cf5d5bf --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/GzipRequestInterceptor.mustache @@ -0,0 +1,70 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import com.squareup.okhttp.*; +import okio.Buffer; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; + +import java.io.IOException; + +/** + * Encodes request bodies using gzip. + * + * Taken from https://github.com/square/okhttp/issues/350 + */ +class GzipRequestInterceptor implements Interceptor { + @Override public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } + + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } + + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() { + return buffer.size(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override public MediaType contentType() { + return body.contentType(); + } + + @Override public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaClientOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaClientOptionsProvider.java index cc0f32174034..3a4ddc208f83 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaClientOptionsProvider.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaClientOptionsProvider.java @@ -23,6 +23,7 @@ public class JavaClientOptionsProvider extends JavaOptionsProvider { options.put(JavaClientCodegen.SUPPORT_JAVA6, "false"); options.put(JavaClientCodegen.USE_BEANVALIDATION, "false"); options.put(JavaClientCodegen.PERFORM_BEANVALIDATION, PERFORM_BEANVALIDATION); + options.put(JavaClientCodegen.USE_GZIP_FEATURE, "false"); return options; } diff --git a/samples/client/petstore/java/jersey1/src/main/java/io/swagger/client/ApiClient.java b/samples/client/petstore/java/jersey1/src/main/java/io/swagger/client/ApiClient.java index de98188515a8..90f05be00780 100644 --- a/samples/client/petstore/java/jersey1/src/main/java/io/swagger/client/ApiClient.java +++ b/samples/client/petstore/java/jersey1/src/main/java/io/swagger/client/ApiClient.java @@ -21,6 +21,7 @@ import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.client.filter.GZIPContentEncodingFilter; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.api.client.WebResource.Builder; @@ -111,6 +112,7 @@ public class ApiClient { DefaultClientConfig conf = new DefaultClientConfig(); conf.getSingletons().add(jsonProvider); Client client = Client.create(conf); + client.addFilter(new GZIPContentEncodingFilter(false)); if (debugging) { client.addFilter(new LoggingFilter()); } diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/ApiClient.java b/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/ApiClient.java index d014d7d4035c..64e0e9a04a96 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/ApiClient.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/ApiClient.java @@ -139,6 +139,7 @@ public class ApiClient { public ApiClient() { httpClient = new OkHttpClient(); + verifyingSsl = true; json = new JSON(this); diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/GzipRequestInterceptor.java b/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/GzipRequestInterceptor.java new file mode 100644 index 000000000000..cc4e4f951c1b --- /dev/null +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/GzipRequestInterceptor.java @@ -0,0 +1,81 @@ +/* + * Swagger Petstore + * This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + * + * OpenAPI spec version: 1.0.0 + * Contact: apiteam@swagger.io + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +package io.swagger.client; + +import com.squareup.okhttp.*; +import okio.Buffer; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; + +import java.io.IOException; + +/** + * Encodes request bodies using gzip. + * + * Taken from https://github.com/square/okhttp/issues/350 + */ +class GzipRequestInterceptor implements Interceptor { + @Override public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } + + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } + + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() { + return buffer.size(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override public MediaType contentType() { + return body.contentType(); + } + + @Override public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } +} \ No newline at end of file