Skip to content

Commit c72c95e

Browse files
committed
fix: handle 204 No Content responses across language clients
Add status code and empty body guards to prevent deserialization crashes on HTTP 204: - JS: response.status === 204 || content.length === 0 in helpers.ts - Java: restore contentLength() == 0, add response.code() == 204 in HttpRequester - Scala: response.code == 204 check in HttpRequester - PHP: 204 === $statusCode || $body === '' before json_decode - Dart: response.statusCode == 204 ? null : response.body - Kotlin: response.status.value == 204 || contentLength() == 0L
1 parent 9088c98 commit c72c95e

6 files changed

Lines changed: 26 additions & 16 deletions

File tree

  • clients
    • algoliasearch-client-dart/packages/client_core/lib/src/transport
    • algoliasearch-client-javascript/packages/client-common/src/transporter
    • algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal
    • algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal
    • algoliasearch-client-php/lib/RetryStrategy
    • algoliasearch-client-scala/src/main/scala/algoliasearch/internal

clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ final class RetryStrategy {
4848
);
4949

5050
/// Run an request and get a response.
51-
Future<Map<String, dynamic>> execute({
51+
Future<Map<String, dynamic>?> execute({
5252
required ApiRequest request,
5353
RequestOptions? options,
5454
}) async {
@@ -66,7 +66,7 @@ final class RetryStrategy {
6666
final response = await requester.perform(httpRequest);
6767
host.reset();
6868
requester.setConnectTimeout(requesterConnectTimeout);
69-
return response.body ?? const {};
69+
return response.statusCode == 204 ? null : response.body;
7070
} on AlgoliaTimeoutException catch (e) {
7171
host.timedOut();
7272
errors.add(e);

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ private <T> T execute(@Nonnull HttpRequest httpRequest, RequestOptions requestOp
9393
}
9494

9595
// Return null if there's no content or the return type isn't provided.
96-
if (returnType == null || response.body() == null || response.body().contentLength() == 0) {
97-
return null; // No need to deserialize, either no content or no type provided
96+
if (returnType == null || response.body() == null || response.body().contentLength() == 0 || response.code() == 204) {
97+
return null;
9898
}
9999

100100
// Returns the raw response when using `*WithHTTPInfo` methods.

clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ export function serializeHeaders(
7575
}
7676

7777
export function deserializeSuccess<TObject>(response: Response): TObject {
78+
// Handle 204 No Content and other empty responses
79+
if (response.status === 204 || response.content.length === 0) {
80+
return undefined as unknown as TObject;
81+
}
82+
7883
try {
7984
return JSON.parse(response.content);
8085
} catch (e) {

clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal/KtorRequester.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public class KtorRequester(
6767
requestBuilder.setTimeout(requestOptions, callType, host)
6868
try {
6969
val response = httpClient.request(requestBuilder)
70-
val body = response.body<T>(returnType)
70+
@Suppress("UNCHECKED_CAST")
71+
val body: T = if (response.status.value == 204 || response.contentLength() == 0L) null as T else response.body<T>(returnType)
7172
mutex.withLock { host.reset() }
7273
return body
7374
} catch (exception: Throwable) {

clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,19 @@ private function handleResponse(
284284
throw new AlgoliaException($statusCode.': '.$response->getReasonPhrase(), $statusCode);
285285
}
286286

287-
try {
288-
$deserializeStart = microtime(true);
289-
$responseArray = Helpers::json_decode($body, true);
290-
$deserializeDurationMs = round((microtime(true) - $deserializeStart) * 1000);
291-
$this->log(LogLevel::DEBUG, 'Response body deserialized in '.$deserializeDurationMs.'ms');
292-
} catch (\InvalidArgumentException $e) {
293-
$this->log(LogLevel::ERROR, 'Failed to deserialize response: '.$e->getMessage());
294-
295-
throw $e;
287+
if (204 === $statusCode || $body === '') {
288+
$responseArray = null;
289+
} else {
290+
try {
291+
$deserializeStart = microtime(true);
292+
$responseArray = Helpers::json_decode($body, true);
293+
$deserializeDurationMs = round((microtime(true) - $deserializeStart) * 1000);
294+
$this->log(LogLevel::DEBUG, 'Response body deserialized in '.$deserializeDurationMs.'ms');
295+
} catch (\InvalidArgumentException $e) {
296+
$this->log(LogLevel::ERROR, 'Failed to deserialize response: '.$e->getMessage());
297+
298+
throw $e;
299+
}
296300
}
297301

298302
if (404 === $statusCode) {

clients/algoliasearch-client-scala/src/main/scala/algoliasearch/internal/HttpRequester.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ private[algoliasearch] class HttpRequester private (
161161
// Handle unsuccessful responses.
162162
if (!response.isSuccessful)
163163
throw AlgoliaApiException(message = response.message, httpErrorCode = response.code)
164-
// Deserialize and return the response.
165-
jsonSerializer.deserialize[T](response.body.byteStream)
164+
if (response.code == 204) null.asInstanceOf[T]
165+
else jsonSerializer.deserialize[T](response.body.byteStream)
166166
} catch {
167167
case exception: IOException => throw AlgoliaClientException(cause = exception)
168168
case exception: AlgoliaApiException =>

0 commit comments

Comments
 (0)