11package io .github .hikingc .matrixsdk .services .utils ;
22
33import io .github .hikingc .matrixsdk .exceptions .*;
4+ import java .io .ByteArrayInputStream ;
45import java .io .IOException ;
56import java .net .URI ;
67import java .net .URISyntaxException ;
78import java .net .URLEncoder ;
89import java .net .http .HttpClient ;
10+ import java .net .http .HttpHeaders ;
911import java .net .http .HttpRequest ;
1012import java .net .http .HttpResponse ;
1113import java .nio .charset .StandardCharsets ;
1214import java .nio .file .Files ;
1315import java .nio .file .Path ;
14- import java .util .Arrays ;
1516import java .util .List ;
1617import java .util .Map ;
1718import java .util .Objects ;
1819import java .util .stream .Collectors ;
1920import java .util .stream .Stream ;
21+ import java .util .zip .GZIPInputStream ;
2022import org .jspecify .annotations .NullMarked ;
2123import org .jspecify .annotations .Nullable ;
2224import org .slf4j .Logger ;
@@ -52,21 +54,20 @@ public HttpTransport(HttpClient httpClient) {
5254 ///
5355 /// @param response the client response.
5456 /// @throws MatrixApiException when the server responds with an unsuccessful HTTP Code.
55- private void validateResponse (HttpResponse <byte []> response ) {
56- var code = response .statusCode ();
57- var body = response .body ();
58- var headers = response .headers ();
57+ private void validateResponse (byte [] response , int code , HttpHeaders headers ) {
58+ var body = new String (response , StandardCharsets .UTF_8 );
59+
5960 logger .debug ("Validate response code: {}, for body: {} and headers: {}" , code , body , headers );
6061 if (code >= 200 && code < 300 ) {
6162 return ;
6263 }
6364
64- if (Arrays . toString ( body ) .isBlank ()) {
65+ if (body .isBlank ()) {
6566 throw new MatrixException ("Server returned with HTTP Code:" + code );
6667 }
6768 ErrorResponse errorResponse ;
6869 try {
69- errorResponse = Mapper .getObjectFromString (body , ErrorResponse .class );
70+ errorResponse = Mapper .getObjectFromString (response , ErrorResponse .class );
7071 } catch (MatrixSerializationException e ) {
7172 throw new MatrixApiException (
7273 "Server returned unparseable error body, HTTP code: " + code , code , e );
@@ -99,13 +100,15 @@ private void validateResponse(HttpResponse<byte[]> response) {
99100 /// @throws MatrixApiException when the response from the server is not successful.
100101 public byte [] getRequest (URI path , @ Nullable String authToken ) {
101102 var builderRequest =
102- HttpRequest .newBuilder ().uri (path ).header (CONTENT_TYPE , APPLICATION_JSON ).GET ();
103-
103+ HttpRequest .newBuilder ()
104+ .uri (path )
105+ .header (CONTENT_TYPE , APPLICATION_JSON )
106+ .header ("Accept-Encoding" , "gzip" )
107+ .GET ();
104108 if (authToken != null ) {
105109 builderRequest .header (AUTHORIZATION , BEARER + authToken );
106110 }
107111 var request = builderRequest .build ();
108-
109112 HttpResponse <byte []> response ;
110113 try {
111114 response = client .send (request , HttpResponse .BodyHandlers .ofByteArray ());
@@ -116,8 +119,9 @@ public byte[] getRequest(URI path, @Nullable String authToken) {
116119 Thread .currentThread ().interrupt ();
117120 throw new MatrixInterruptedException ("This request has been interrupted" , e );
118121 }
119- this .validateResponse (response );
120- return response .body ();
122+ var uncResponse = decompressIfNeeded (response );
123+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
124+ return uncResponse ;
121125 }
122126
123127 /// Sends a `POST` request to the given endpoint.
@@ -130,7 +134,7 @@ public byte[] getRequest(URI path, @Nullable String authToken) {
130134 /// @throws MatrixInterruptedException if the operation has been interrupted.
131135 /// @throws MatrixApiException when the response from the server is not successful.
132136 public byte [] postRequest (URI path , @ Nullable String body , @ Nullable String authToken ) {
133- var builderRequest = HttpRequest .newBuilder ().uri (path );
137+ var builderRequest = HttpRequest .newBuilder ().header ( "Accept-Encoding" , "gzip" ). uri (path );
134138
135139 if (body != null ) {
136140 builderRequest .header (CONTENT_TYPE , APPLICATION_JSON );
@@ -156,8 +160,9 @@ public byte[] postRequest(URI path, @Nullable String body, @Nullable String auth
156160 Thread .currentThread ().interrupt ();
157161 throw new MatrixInterruptedException ("This request has been interrupted" , e );
158162 }
159- this .validateResponse (response );
160- return response .body ();
163+ var uncResponse = decompressIfNeeded (response );
164+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
165+ return uncResponse ;
161166 }
162167
163168 /// Sends a `POST` request to the given endpoint.
@@ -172,7 +177,7 @@ public byte[] postRequest(URI path, @Nullable String body, @Nullable String auth
172177 /// with unsuccessful HTTP Code.
173178 /// @throws MatrixApiException when the response from the server is not successful.
174179 public byte [] postAuth (URI path , @ Nullable String body ) {
175- var builderRequest = HttpRequest .newBuilder ().uri (path );
180+ var builderRequest = HttpRequest .newBuilder ().header ( "Accept-Encoding" , "gzip" ). uri (path );
176181
177182 builderRequest .header (CONTENT_TYPE , "application/x-www-form-urlencoded" );
178183
@@ -193,8 +198,9 @@ public byte[] postAuth(URI path, @Nullable String body) {
193198 Thread .currentThread ().interrupt ();
194199 throw new MatrixInterruptedException ("This request has been interrupted" , e );
195200 }
196- this .validateResponse (response );
197- return response .body ();
201+ var uncResponse = decompressIfNeeded (response );
202+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
203+ return uncResponse ;
198204 }
199205
200206 /// Sends a `PUT` request to the given endpoint.
@@ -211,6 +217,7 @@ public byte[] putRequest(URI path, @Nullable String body, String authToken) {
211217
212218 var builderRequest =
213219 HttpRequest .newBuilder ()
220+ .header ("Accept-Encoding" , "gzip" )
214221 .uri (path )
215222 .headers (AUTHORIZATION , BEARER + authToken , CONTENT_TYPE , APPLICATION_JSON );
216223
@@ -230,8 +237,9 @@ public byte[] putRequest(URI path, @Nullable String body, String authToken) {
230237 Thread .currentThread ().interrupt ();
231238 throw new MatrixInterruptedException ("This request has been interrupted" , e );
232239 }
233- this .validateResponse (response );
234- return response .body ();
240+ var uncResponse = decompressIfNeeded (response );
241+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
242+ return uncResponse ;
235243 }
236244
237245 /// Sends a `PUT` request to the given endpoint to upload a resource.
@@ -251,6 +259,7 @@ public byte[] putResource(URI path, Path resource, String authToken) {
251259 try {
252260 uploadRequest =
253261 HttpRequest .newBuilder ()
262+ .header ("Accept-Encoding" , "gzip" )
254263 .uri (path )
255264 .headers (
256265 AUTHORIZATION , BEARER + authToken , CONTENT_TYPE , Files .probeContentType (resource ))
@@ -271,8 +280,9 @@ public byte[] putResource(URI path, Path resource, String authToken) {
271280 Thread .currentThread ().interrupt ();
272281 throw new MatrixInterruptedException ("This request has been interrupted" , e );
273282 }
274- this .validateResponse (response );
275- return response .body ();
283+ var uncResponse = decompressIfNeeded (response );
284+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
285+ return uncResponse ;
276286 }
277287
278288 /// Sends a `DELETE` request to the given endpoint.
@@ -287,6 +297,7 @@ public byte[] putResource(URI path, Path resource, String authToken) {
287297 public byte [] deleteRequest (URI path , String authToken ) {
288298 HttpRequest deleteRequest =
289299 HttpRequest .newBuilder ()
300+ .header ("Accept-Encoding" , "gzip" )
290301 .uri (path )
291302 .header (AUTHORIZATION , BEARER + authToken )
292303 .DELETE ()
@@ -302,8 +313,9 @@ public byte[] deleteRequest(URI path, String authToken) {
302313 Thread .currentThread ().interrupt ();
303314 throw new MatrixInterruptedException ("This request has been interrupted" , e );
304315 }
305- this .validateResponse (response );
306- return response .body ();
316+ var uncResponse = decompressIfNeeded (response );
317+ this .validateResponse (uncResponse , response .statusCode (), response .headers ());
318+ return uncResponse ;
307319 }
308320
309321 /// URL-encodes a string using UTF-8.
@@ -333,6 +345,23 @@ public URI generateEncodedURI(String baseUrl, String path, @Nullable Map<String,
333345 }
334346 }
335347
348+ /// Method to check if the server did return response as GZIP and then decompress it.
349+ ///
350+ /// @param response an HTTP Response.
351+ /// @return a `byte` array.
352+ private byte [] decompressIfNeeded (HttpResponse <byte []> response ) {
353+ String encoding = response .headers ().firstValue ("Content-Encoding" ).orElse ("" );
354+ if (!encoding .equalsIgnoreCase ("gzip" )) {
355+ return response .body ();
356+ }
357+ try (var gzip = new GZIPInputStream (new ByteArrayInputStream (response .body ()))) {
358+ logger .debug ("Server has returned with GZIP compression. Decompressing response" );
359+ return gzip .readAllBytes ();
360+ } catch (IOException e ) {
361+ throw new MatrixIOException ("Failed to decompress gzip response body" , e );
362+ }
363+ }
364+
336365 /// Builds a [URI] from a base, path, and query parameters. This method WON'T encode to UTF-8 the
337366 /// queries
338367 ///
0 commit comments