Skip to content

Commit 3bcfcbf

Browse files
adding checks for json end_document in http transport (#5895)
* adding checks for json end_document in http transport * moving json end of document parsing to toApolloReponse and turning it into an explicit error, as per PR feedback * catch exceptions, add some KDoc --------- Co-authored-by: Martin Bonnin <[email protected]>
1 parent 8c97198 commit 3bcfcbf

File tree

1 file changed

+59
-14
lines changed
  • libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api

1 file changed

+59
-14
lines changed

libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operations.kt

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ fun <D : Operation.Data> Operation<D>.parseJsonResponse(
9595
* ```
9696
*
9797
* By default, this method does not close the [jsonReader]
98+
*
99+
* @see [toApolloResponse]
98100
*/
99101
@JvmOverloads
100102
fun <D : Operation.Data> Operation<D>.parseResponse(
@@ -112,21 +114,24 @@ fun <D : Operation.Data> Operation<D>.parseResponse(
112114
deferredFragmentIdentifiers,
113115
)
114116
} catch (throwable: Throwable) {
115-
val apolloException = if (throwable is ApolloException) {
116-
throwable
117-
} else {
118-
ApolloNetworkException(
119-
message = "Error while reading JSON response",
120-
platformCause = throwable
121-
)
122-
}
123-
return ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = this)
124-
.exception(exception = apolloException)
117+
ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = this)
118+
.exception(exception = throwable.wrapIfNeeded())
125119
.isLast(true)
126120
.build()
127121
}
128122
}
129123

124+
private fun Throwable.wrapIfNeeded(): ApolloException {
125+
return if (this is ApolloException) {
126+
this
127+
} else {
128+
ApolloNetworkException(
129+
message = "Error while reading JSON response",
130+
platformCause = this
131+
)
132+
}
133+
}
134+
130135
/**
131136
* writes a successful GraphQL Json response containing "data" to the given sink.
132137
*
@@ -160,9 +165,12 @@ fun <D : Operation.Data> Operation<D>.composeJsonResponse(
160165
}
161166

162167
/**
163-
* Parses the [JsonReader] into an [ApolloResponse]
168+
* Reads a single [ApolloResponse] from [this]. Returns an error response if [this] contains
169+
* more than one JSON response or trailing tokens.
170+
* [toApolloResponse] takes ownership and closes [this].
164171
*
165-
* Warning: this closes the [JsonReader]. If you need to reuse it, use [parseResponse]
172+
* @return the parsed [ApolloResponse]
173+
* @see parseResponse
166174
*/
167175
@ApolloExperimental
168176
fun <D : Operation.Data> JsonReader.toApolloResponse(
@@ -172,17 +180,54 @@ fun <D : Operation.Data> JsonReader.toApolloResponse(
172180
deferredFragmentIdentifiers: Set<DeferredFragmentIdentifier>? = null,
173181
): ApolloResponse<D> {
174182
return use {
175-
operation.parseResponse(it, requestUuid, customScalarAdapters, deferredFragmentIdentifiers)
183+
try {
184+
ResponseParser.parse(
185+
this,
186+
operation,
187+
requestUuid,
188+
customScalarAdapters,
189+
deferredFragmentIdentifiers,
190+
).also {
191+
if (peek() != JsonReader.Token.END_DOCUMENT) {
192+
throw JsonDataException("Expected END_DOCUMENT but was ${peek()}")
193+
}
194+
}
195+
} catch (throwable: Throwable) {
196+
ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = operation)
197+
.exception(exception = throwable.wrapIfNeeded())
198+
.isLast(true)
199+
.build()
200+
}
176201
}
177202
}
178203

204+
/**
205+
* Reads a [ApolloResponse] from [this].
206+
* The caller is responsible for closing [this].
207+
*
208+
* @return the parsed [ApolloResponse]
209+
* @see [toApolloResponse]
210+
*/
179211
@ApolloExperimental
180212
fun <D : Operation.Data> JsonReader.parseResponse(
181213
operation: Operation<D>,
182214
requestUuid: Uuid? = null,
183215
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
184216
deferredFragmentIdentifiers: Set<DeferredFragmentIdentifier>? = null,
185217
): ApolloResponse<D> {
186-
return operation.parseResponse(this, requestUuid, customScalarAdapters, deferredFragmentIdentifiers)
218+
return try {
219+
ResponseParser.parse(
220+
this,
221+
operation,
222+
requestUuid,
223+
customScalarAdapters,
224+
deferredFragmentIdentifiers,
225+
)
226+
} catch (throwable: Throwable) {
227+
ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = operation)
228+
.exception(exception = throwable.wrapIfNeeded())
229+
.isLast(true)
230+
.build()
231+
}
187232
}
188233

0 commit comments

Comments
 (0)