Skip to content
This repository was archived by the owner on Feb 1, 2023. It is now read-only.

Commit 2029585

Browse files
Merge pull request #40 from snyk/feat/code-encode
feat: encode and compress POST/PUT requests to Code API [ROAD-909]
2 parents c036ff9 + c82c33d commit 2029585

File tree

3 files changed

+160
-42
lines changed

3 files changed

+160
-42
lines changed

src/main/java/ai/deepcode/javaclient/DeepCodeRestApiImpl.java

+46-42
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ai.deepcode.javaclient;
22

3+
import ai.deepcode.javaclient.core.Base64EncodeRequestInterceptor;
34
import ai.deepcode.javaclient.requests.ExtendBundleWithContentRequest;
45
import ai.deepcode.javaclient.requests.ExtendBundleWithHashRequest;
56
import ai.deepcode.javaclient.requests.FileContentRequest;
@@ -47,7 +48,7 @@ public class DeepCodeRestApiImpl implements DeepCodeRestApi {
4748

4849
// Create simple REST adapter which points the baseUrl.
4950
private static Retrofit buildRetrofit(
50-
String baseUrl, boolean disableSslVerification, boolean requestLogging) {
51+
String baseUrl, boolean disableSslVerification, boolean requestLogging) {
5152
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
5253
// set your desired log level
5354
if (requestLogging) {
@@ -57,15 +58,16 @@ private static Retrofit buildRetrofit(
5758
}
5859

5960
OkHttpClient.Builder builder =
60-
new OkHttpClient.Builder()
61-
.connectTimeout(100, TimeUnit.SECONDS)
62-
.writeTimeout(100, TimeUnit.SECONDS)
63-
.readTimeout(100, TimeUnit.SECONDS)
64-
.addInterceptor(logging);
61+
new OkHttpClient.Builder()
62+
.connectTimeout(100, TimeUnit.SECONDS)
63+
.writeTimeout(100, TimeUnit.SECONDS)
64+
.readTimeout(100, TimeUnit.SECONDS)
65+
.addInterceptor(logging)
66+
.addInterceptor(new Base64EncodeRequestInterceptor());
6567

6668
if (disableSslVerification) {
6769
X509TrustManager x509TrustManager = buildUnsafeTrustManager();
68-
final TrustManager[] trustAllCertificates = new TrustManager[] {x509TrustManager};
70+
final TrustManager[] trustAllCertificates = new TrustManager[]{x509TrustManager};
6971

7072
try {
7173
final String sslProtocol = "SSL";
@@ -82,24 +84,26 @@ private static Retrofit buildRetrofit(
8284

8385
OkHttpClient client = builder.build();
8486
return new Retrofit.Builder()
85-
.baseUrl(baseUrl)
86-
.client(client)
87-
.addConverterFactory(GsonConverterFactory.create())
88-
.build();
87+
.baseUrl(baseUrl)
88+
.client(client)
89+
.addConverterFactory(GsonConverterFactory.create())
90+
.build();
8991
}
9092

9193
@NotNull
9294
private static X509TrustManager buildUnsafeTrustManager() {
9395
return new X509TrustManager() {
9496
@Override
95-
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
97+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
98+
}
9699

97100
@Override
98-
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
101+
public void checkServerTrusted(X509Certificate[] chain, String authType) {
102+
}
99103

100104
@Override
101105
public X509Certificate[] getAcceptedIssuers() {
102-
return new X509Certificate[] {};
106+
return new X509Certificate[]{};
103107
}
104108
};
105109
}
@@ -108,28 +112,28 @@ public X509Certificate[] getAcceptedIssuers() {
108112
* Re-set baseUrl for retrofit instance
109113
*
110114
* @param baseUrl new baseUrl. <b>Null</b> or empty "" value will reset to default {@code
111-
* #API_URL}
115+
* #API_URL}
112116
*/
113117
@Override
114118
public void setBaseUrl(
115119
@Nullable String baseUrl, boolean disableSslVerification, boolean requestLogging) {
116120
retrofit =
117-
buildRetrofit(
118-
(baseUrl == null || baseUrl.isEmpty()) ? API_URL : baseUrl,
119-
disableSslVerification,
120-
requestLogging);
121+
buildRetrofit(
122+
(baseUrl == null || baseUrl.isEmpty()) ? API_URL : baseUrl,
123+
disableSslVerification,
124+
requestLogging);
121125
}
122126

123127
private interface CreateBundleCall {
124128
@retrofit2.http.Headers("Content-Type: application/json")
125129
@POST("bundle")
126130
Call<CreateBundleResponse> doCreateBundle(
127-
@Header("Session-Token") String token, @Body FileContentRequest files);
131+
@Header("Session-Token") String token, @Body FileContentRequest files);
128132

129133
@retrofit2.http.Headers("Content-Type: application/json")
130134
@POST("bundle")
131135
Call<CreateBundleResponse> doCreateBundle(
132-
@Header("Session-Token") String token, @Body FileHashRequest files);
136+
@Header("Session-Token") String token, @Body FileHashRequest files);
133137
}
134138

135139
private static <Req> CreateBundleResponse doCreateBundle(String token, Req request) {
@@ -138,10 +142,10 @@ private static <Req> CreateBundleResponse doCreateBundle(String token, Req reque
138142
try {
139143
if (request instanceof FileContentRequest)
140144
retrofitResponse =
141-
createBundleCall.doCreateBundle(token, (FileContentRequest) request).execute();
145+
createBundleCall.doCreateBundle(token, (FileContentRequest) request).execute();
142146
else if (request instanceof FileHashRequest)
143147
retrofitResponse =
144-
createBundleCall.doCreateBundle(token, (FileHashRequest) request).execute();
148+
createBundleCall.doCreateBundle(token, (FileHashRequest) request).execute();
145149
else throw new IllegalArgumentException();
146150
} catch (IOException e) {
147151
return new CreateBundleResponse();
@@ -200,8 +204,8 @@ private interface CheckBundleCall {
200204
// @retrofit2.http.Headers("Content-Type: application/json")
201205
@GET("bundle/{bundleId}")
202206
Call<CreateBundleResponse> doCheckBundle(
203-
@Header("Session-Token") String token,
204-
@Path(value = "bundleId", encoded = true) String bundleId);
207+
@Header("Session-Token") String token,
208+
@Path(value = "bundleId", encoded = true) String bundleId);
205209
}
206210

207211
/**
@@ -249,16 +253,16 @@ private interface ExtendBundleCall {
249253
@retrofit2.http.Headers("Content-Type: application/json")
250254
@PUT("bundle/{bundleId}")
251255
Call<CreateBundleResponse> doExtendBundle(
252-
@Header("Session-Token") String token,
253-
@Path(value = "bundleId", encoded = true) String bundleId,
254-
@Body ExtendBundleWithHashRequest extendBundleWithHashRequest);
256+
@Header("Session-Token") String token,
257+
@Path(value = "bundleId", encoded = true) String bundleId,
258+
@Body ExtendBundleWithHashRequest extendBundleWithHashRequest);
255259

256260
@retrofit2.http.Headers("Content-Type: application/json")
257261
@PUT("bundle/{bundleId}")
258262
Call<CreateBundleResponse> doExtendBundle(
259-
@Header("Session-Token") String token,
260-
@Path(value = "bundleId", encoded = true) String bundleId,
261-
@Body ExtendBundleWithContentRequest extendBundleWithContentRequest);
263+
@Header("Session-Token") String token,
264+
@Path(value = "bundleId", encoded = true) String bundleId,
265+
@Body ExtendBundleWithContentRequest extendBundleWithContentRequest);
262266
}
263267

264268
/**
@@ -276,14 +280,14 @@ public <Req> CreateBundleResponse extendBundle(
276280
try {
277281
if (request instanceof ExtendBundleWithHashRequest)
278282
retrofitResponse =
279-
extendBundleCall
280-
.doExtendBundle(token, bundleId, (ExtendBundleWithHashRequest) request)
281-
.execute();
283+
extendBundleCall
284+
.doExtendBundle(token, bundleId, (ExtendBundleWithHashRequest) request)
285+
.execute();
282286
else if (request instanceof ExtendBundleWithContentRequest)
283287
retrofitResponse =
284-
extendBundleCall
285-
.doExtendBundle(token, bundleId, (ExtendBundleWithContentRequest) request)
286-
.execute();
288+
extendBundleCall
289+
.doExtendBundle(token, bundleId, (ExtendBundleWithContentRequest) request)
290+
.execute();
287291
else throw new IllegalArgumentException();
288292
} catch (IOException e) {
289293
return new CreateBundleResponse();
@@ -299,7 +303,7 @@ else if (request instanceof ExtendBundleWithContentRequest)
299303
break;
300304
case 400:
301305
result.setStatusDescription(
302-
"Attempted to extend a git bundle, or ended up with an empty bundle after the extension");
306+
"Attempted to extend a git bundle, or ended up with an empty bundle after the extension");
303307
break;
304308
case 401:
305309
result.setStatusDescription("Missing sessionToken or incomplete login process");
@@ -324,7 +328,7 @@ private interface GetAnalysisCall {
324328
@retrofit2.http.Headers("Content-Type: application/json")
325329
@POST("analysis")
326330
Call<GetAnalysisResponse> doGetAnalysis(
327-
@Header("Session-Token") String token, @Body GetAnalysisRequest filesToAnalyse);
331+
@Header("Session-Token") String token, @Body GetAnalysisRequest filesToAnalyse);
328332
}
329333

330334
/**
@@ -346,9 +350,9 @@ public GetAnalysisResponse getAnalysis(
346350
GetAnalysisCall getAnalysisCall = retrofit.create(GetAnalysisCall.class);
347351
try {
348352
Response<GetAnalysisResponse> retrofitResponse =
349-
getAnalysisCall
350-
.doGetAnalysis(token, new GetAnalysisRequest(bundleId, filesToAnalyse, severity, shard, ideProductName, orgDisplayName))
351-
.execute();
353+
getAnalysisCall
354+
.doGetAnalysis(token, new GetAnalysisRequest(bundleId, filesToAnalyse, severity, shard, ideProductName, orgDisplayName))
355+
.execute();
352356
GetAnalysisResponse result = retrofitResponse.body();
353357
if (result == null) result = new GetAnalysisResponse();
354358
result.setStatusCode(retrofitResponse.code());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package ai.deepcode.javaclient.core;
2+
3+
import okhttp3.Interceptor;
4+
import okhttp3.MediaType;
5+
import okhttp3.Request;
6+
import okhttp3.RequestBody;
7+
import okhttp3.Response;
8+
import okio.Buffer;
9+
import org.jetbrains.annotations.NotNull;
10+
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.IOException;
13+
import java.util.Base64;
14+
import java.util.Objects;
15+
import java.util.zip.GZIPOutputStream;
16+
17+
public class Base64EncodeRequestInterceptor implements Interceptor {
18+
private static final String CONTENT_ENCODING_HEADER = "Content-Encoding";
19+
private static final String MEDIA_TYPE_OCTET_STREAM = "application/octet-stream";
20+
private static final String MEDIA_TYPE_OCTET_STREAM_GZIP = "gzip";
21+
22+
@Override
23+
public @NotNull Response intercept(Interceptor.Chain chain) throws IOException {
24+
Request originalRequest = chain.request();
25+
Request.Builder builder = originalRequest.newBuilder();
26+
27+
if (originalRequest.method().equalsIgnoreCase("POST") ||
28+
originalRequest.method().equalsIgnoreCase("PUT")) {
29+
30+
try (Buffer buffer = new Buffer()) {
31+
Objects.requireNonNull(originalRequest.body()).writeTo(buffer);
32+
33+
byte[] encoded = encodeToBase64(buffer.readByteArray());
34+
byte[] compressed = compress(encoded);
35+
36+
RequestBody body = RequestBody.create(compressed, MediaType.parse(MEDIA_TYPE_OCTET_STREAM));
37+
38+
builder = originalRequest.newBuilder()
39+
.header(CONTENT_ENCODING_HEADER, MEDIA_TYPE_OCTET_STREAM_GZIP)
40+
.method(originalRequest.method(), body);
41+
}
42+
}
43+
44+
Request request = builder.build();
45+
46+
return chain.proceed(request);
47+
}
48+
49+
byte[] encodeToBase64(byte[] bytes) {
50+
return Base64.getEncoder().encode(bytes);
51+
}
52+
53+
byte[] compress(byte[] bytes) throws IOException {
54+
byte[] compressed;
55+
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);
56+
GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
57+
gzip.write(bytes);
58+
gzip.finish();
59+
compressed = bos.toByteArray();
60+
}
61+
return compressed;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package ai.deepcode.javaclient.core;
2+
3+
import org.junit.Test;
4+
5+
import java.io.ByteArrayInputStream;
6+
import java.io.ByteArrayOutputStream;
7+
import java.io.IOException;
8+
import java.util.zip.GZIPInputStream;
9+
10+
import static org.junit.Assert.assertEquals;
11+
12+
public class Base64EncodeRequestInterceptorTest {
13+
14+
@Test
15+
public void encodesCorrectly() {
16+
String jsonStr = "{\"src/app.ts\"}:{\"console.log(\\\"hello\\\"}\"\n";
17+
Base64EncodeRequestInterceptor interceptor = new Base64EncodeRequestInterceptor();
18+
19+
byte[] encodedBytes = interceptor.encodeToBase64(jsonStr.getBytes());
20+
21+
assertEquals("eyJzcmMvYXBwLnRzIn06eyJjb25zb2xlLmxvZyhcImhlbGxvXCJ9Igo=", new String(encodedBytes));
22+
}
23+
24+
@Test
25+
public void compressesCorrectly() throws IOException {
26+
String base64Str = "eyJzcmMvYXBwLnRzIn06eyJjb25zb2xlLmxvZyhcImhlbGxvXCJ9Igo=";
27+
Base64EncodeRequestInterceptor interceptor = new Base64EncodeRequestInterceptor();
28+
29+
byte[] compressedBytes = interceptor.compress(base64Str.getBytes());
30+
31+
assertEquals(base64Str, new String(decompress(compressedBytes)));
32+
}
33+
34+
private byte[] decompress(byte[] gzip) throws IOException {
35+
try (
36+
ByteArrayInputStream byteIn = new ByteArrayInputStream(gzip);
37+
GZIPInputStream gzIn = new GZIPInputStream(byteIn);
38+
ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {
39+
int res = 0;
40+
byte[] buf = new byte[1024];
41+
while (res >= 0) {
42+
res = gzIn.read(buf, 0, buf.length);
43+
if (res > 0) {
44+
byteOut.write(buf, 0, res);
45+
}
46+
}
47+
return byteOut.toByteArray();
48+
}
49+
}
50+
}
51+

0 commit comments

Comments
 (0)