2424import org .apache .hc .core5 .util .Timeout ;
2525import org .cloudinary .json .JSONException ;
2626import org .cloudinary .json .JSONObject ;
27+ import org .cloudinary .json .JSONTokener ;
2728
2829import java .io .IOException ;
30+ import java .io .InputStream ;
31+ import java .io .InputStreamReader ;
2932import java .lang .reflect .Constructor ;
3033import java .net .URISyntaxException ;
3134import java .nio .charset .StandardCharsets ;
35+ import java .util .Collections ;
3236import java .util .List ;
3337import java .util .Map ;
3438
@@ -83,55 +87,72 @@ public ApiResponse callApi(Api.HttpMethod method, String apiUrl, Map<String, ?>
8387
8488 request .setHeader ("Authorization" , autorizationHeader );
8589
86- return getApiResponse (request );
90+ return getApiResponse (request , options );
8791 }
8892
89- private ApiResponse getApiResponse (HttpUriRequestBase request ) throws Exception {
90- String responseData = null ;
91- int code = 0 ;
92- CloseableHttpResponse response ;
93- try {
94- response = client .execute (request );
95- code = response .getCode ();
93+ @ SuppressWarnings ({"rawtypes" , "unchecked" })
94+ private ApiResponse getApiResponse (HttpUriRequestBase request , Map options ) throws Exception {
95+ boolean skipResponseBody = options != null && ObjectUtils .asBoolean (options .get ("skip_response_body" ), false );
96+
97+ try (CloseableHttpResponse response = client .execute (request )) {
98+ int code = response .getCode ();
9699 HttpEntity entity = response .getEntity ();
97- if (entity != null ) {
98- responseData = EntityUtils .toString (entity , StandardCharsets .UTF_8 );
100+
101+ if (code != 200 ) {
102+ String responseData = entity != null ? EntityUtils .toString (entity , StandardCharsets .UTF_8 ) : null ;
103+ throw buildError (code , responseData );
104+ }
105+
106+ if (skipResponseBody ) {
107+ EntityUtils .consumeQuietly (entity );
108+ return new Response (response , Collections .<String , Object >emptyMap ());
109+ }
110+
111+ if (entity == null ) {
112+ throw new RuntimeException ("Invalid JSON response from server: empty response body" );
99113 }
100- } catch (IOException e ) {
101- throw new GeneralError ("Error executing request: " + e .getMessage ());
102- }
103114
104- if ( code != 200 ) {
115+ // Parse straight from the stream (explicit UTF-8) to avoid buffering the whole body as a String.
105116 Map <String , Object > result ;
106- try {
107- JSONObject responseJSON = new JSONObject (responseData );
117+ try ( InputStream content = entity . getContent ()) {
118+ JSONObject responseJSON = new JSONObject (new JSONTokener ( new InputStreamReader ( content , StandardCharsets . UTF_8 )) );
108119 result = ObjectUtils .toMap (responseJSON );
109120 } catch (JSONException e ) {
121+ if (e .getCause () instanceof IOException ) {
122+ // network failure while reading the body: same error type as before streaming the parse
123+ throw new GeneralError ("Error executing request: " + e .getMessage ());
124+ }
110125 throw new RuntimeException ("Invalid JSON response from server " + e .getMessage ());
111126 }
127+ EntityUtils .consumeQuietly (entity );
112128
113- // Extract the error message from the result map
114- String message = (String ) ((Map <String , Object >) result .get ("error" )).get ("message" );
115-
116- // Get the appropriate exception class based on status code
117- Class <? extends Exception > exceptionClass = Api .CLOUDINARY_API_ERROR_CLASSES .get (code );
118- if (exceptionClass != null ) {
119- Constructor <? extends Exception > exceptionConstructor = exceptionClass .getConstructor (String .class );
120- throw exceptionConstructor .newInstance (message );
121- } else {
122- throw new GeneralError ("Server returned unexpected status code - " + code + " - " + responseData );
123- }
129+ return new Response (response , result );
130+ } catch (IOException e ) {
131+ throw new GeneralError ("Error executing request: " + e .getMessage ());
124132 }
133+ }
125134
135+ @ SuppressWarnings ("unchecked" )
136+ private Exception buildError (int code , String responseData ) throws Exception {
126137 Map <String , Object > result ;
138+ if (responseData == null ) {
139+ throw new RuntimeException ("Invalid JSON response from server null" );
140+ }
127141 try {
128- JSONObject responseJSON = new JSONObject (responseData );
129- result = ObjectUtils .toMap (responseJSON );
142+ result = ObjectUtils .toMap (new JSONObject (responseData ));
130143 } catch (JSONException e ) {
131144 throw new RuntimeException ("Invalid JSON response from server " + e .getMessage ());
132145 }
133146
134- return new Response (response , result );
147+ Object error = result .get ("error" );
148+ String message = error instanceof Map ? (String ) ((Map <String , Object >) error ).get ("message" ) : null ;
149+
150+ Class <? extends Exception > exceptionClass = Api .CLOUDINARY_API_ERROR_CLASSES .get (code );
151+ if (exceptionClass != null ) {
152+ Constructor <? extends Exception > exceptionConstructor = exceptionClass .getConstructor (String .class );
153+ return exceptionConstructor .newInstance (message );
154+ }
155+ return new GeneralError ("Server returned unexpected status code - " + code + " - " + responseData );
135156 }
136157
137158 @ Override
@@ -144,7 +165,7 @@ public ApiResponse callAccountApi(Api.HttpMethod method, String apiUrl, Map<Stri
144165 request .setHeader ("Authorization" , authorizationHeader );
145166
146167 // Execute the request and return the response
147- return getApiResponse (request );
168+ return getApiResponse (request , options );
148169 }
149170
150171 private HttpUriRequestBase prepareRequest (Api .HttpMethod method , String apiUrl , Map <String , ? extends Object > params , Map <String , ?> options ) throws URISyntaxException {
0 commit comments