@@ -10,7 +10,9 @@ package org.dexpace.sdk.serde.jackson
1010import com.fasterxml.jackson.core.type.TypeReference
1111import org.dexpace.sdk.core.http.response.Response
1212import org.dexpace.sdk.core.http.response.ResponseHandler
13+ import org.dexpace.sdk.core.http.response.exception.HttpException
1314import org.dexpace.sdk.core.http.response.exception.HttpExceptionFactory
15+ import org.dexpace.sdk.core.http.response.throwOnError
1416import org.dexpace.sdk.core.serde.Serde
1517import org.dexpace.sdk.core.serde.SerdeException
1618
@@ -67,13 +69,14 @@ public fun <T> jsonHandler(
6769 * Status-aware variant of [jsonHandler]: on a 2xx response it decodes the body into [type]; on any
6870 * non-2xx response it throws instead of attempting to deserialize an error payload as [type].
6971 *
70- * A 4xx / 5xx response is mapped through [HttpExceptionFactory] to the matching
71- * [org.dexpace.sdk.core.http.response.exception.HttpException] subclass, which carries the
72- * still-unconsumed error [org.dexpace.sdk.core.http.response.ResponseBody] — the caught exception
73- * owns closing it (e.g. via `bodySnapshot()` or typed error-body deserialization). Any other
74- * non-success status (1xx / 3xx reaching a terminal handler) closes the response and fails with a
75- * [SerdeException]. On the success path the body is consumed and the response closed exactly as the
76- * plain [jsonHandler].
72+ * A 4xx / 5xx response is mapped through [Response.throwOnError] to the matching
73+ * [org.dexpace.sdk.core.http.response.exception.HttpException] subclass, whose error
74+ * [org.dexpace.sdk.core.http.response.ResponseBody] is a **buffered** in-memory copy (bounded to
75+ * 1 MiB) — so it stays readable from the caught exception (e.g. via `bodySnapshot()` or typed
76+ * error-body deserialization) even after this handler runs inside a `response.use { … }` block that
77+ * closes the live response. Any other non-success status (1xx / 3xx reaching a terminal handler)
78+ * closes the response and fails with a [SerdeException]. On the success path the body is consumed
79+ * and the response closed exactly as the plain [jsonHandler].
7780 *
7881 * @param serde The serde whose deserializer decodes the body.
7982 * @param type The non-parametric success target type.
@@ -107,18 +110,19 @@ public fun <T> jsonHandlerOrThrow(
107110 }
108111
109112/* *
110- * Throws when [response] is not a 2xx: a 4xx / 5xx is mapped to its [HttpExceptionFactory] exception
111- * (which retains the live error body for the caller to inspect and close); any other non-success
112- * status closes the response and raises a [SerdeException]. Returns normally on a 2xx so the caller
113- * proceeds to decode.
113+ * Throws when [response] is not a 2xx: a 4xx / 5xx is mapped to its [HttpException] via
114+ * [Response.throwOnError], which buffers a bounded (≤ 1 MiB) copy of the error body into the thrown
115+ * exception so it survives the live response being closed (e.g. by a surrounding `response.use { … }`);
116+ * any other non-success status closes the response and raises a [SerdeException]. Returns normally on
117+ * a 2xx so the caller proceeds to decode.
114118 */
115119private fun throwIfNotSuccess (response : Response ) {
116120 val status = response.status
117121 if (status.isSuccess) return
118122 if (HttpExceptionFactory .isErrorStatus(status.code)) {
119- // Hand the still-unconsumed error body to the mapped HttpException; the caught exception owns
120- // reading and closing it. Do NOT close here or bodySnapshot()/typed error decode would fail .
121- throw HttpExceptionFactory .fromResponse(response )
123+ // Buffers the error body (≤ 1 MiB) into the mapped HttpException and throws, so the caught
124+ // exception's body is readable even after the live response is closed. Always throws here .
125+ response.throwOnError( )
122126 }
123127 response.close()
124128 throw SerdeException (
0 commit comments