Skip to content

Handle invalid JSON HTTP response from Gateway for error scenarios #4162

@ealsur

Description

@ealsur

We have received multiple reports of cases where the SDK throws a JSON serialization exception when processing HTTP responses from the Cosmos DB Gateway, for example:

Exception: Newtonsoft.Json.JsonReaderException: Error reading JObject from JsonReader. Path '', line 0, position 0.   
  at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)    
  at Microsoft.Azure.Documents.JsonSerializable.LoadFrom(JsonReader reader, JsonSerializerSettings serializerSettings)    
  at Microsoft.Azure.Documents.JsonSerializable.LoadFrom[T](JsonTextReader jsonReader, ITypeResolver`1 typeResolver, JsonSerializerSettings settings)    
  at Microsoft.Azure.Cosmos.GatewayStoreClient.<CreateDocumentClientExceptionAsync>d__11.MoveNext() --- End of stack trace from previous location where exception was thrown ---    
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()    
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
  at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()    
  at Microsoft.Azure.Cosmos.GatewayStoreClient.<ParseResponseAsync>d__9.MoveNext()

Microsoft.Azure.Cosmos.GatewayStoreClient.<ParseResponseAsync> hints as this being an HTTP response from Gateway.

Microsoft.Azure.Cosmos.GatewayStoreClient.<CreateDocumentClientExceptionAsync> means the statuscode of the HTTP response was a failure (not 200 or any success).

Which means, Gateway returned a failure but the content was invalid.

SDK checks the Content-Type header and if it's application/json then expects the content to be valid JSON:

if (string.Equals(responseMessage.Content?.Headers?.ContentType?.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
{
Stream readStream = await responseMessage.Content.ReadAsStreamAsync();
Error error = Documents.Resource.LoadFrom<Error>(readStream);

In all these cases what happened was that the content was either empty or invalid JSON.

To avoid the exception in the most common case (empty), we should check the Content-Size of the response, and if it's 0, either throw an exception saying that the response is invalid because it's empty or avoid attempting the JSON deserialization of the body and just produce the error response using the statuscode as if it had no content.

Expected outcome

  • If the Content-Size header is application/json but the content size is 0, skip attempting to deserialize the content
  • If the Content-Size header is application/json but the content size > 0, attempt to deserialize the content. If that fails, follow the code path that treats the content as text (non-JSON path).

Metadata

Metadata

Labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions