Don't eat exceptions from faulted Tasks.
If explicitly testing for IsCompleted then one must also test for IsFaulted and if not handling the exception, return the faulted Task.
This commit is contained in:
@@ -1770,6 +1770,68 @@ null//comment
|
||||
Assert.IsTrue(writer.WriteValueAsync(default(ushort?), token).IsCanceled);
|
||||
Assert.IsTrue(writer.WriteWhitespaceAsync(" ", token).IsCanceled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FailureOnStartWriteProperty()
|
||||
{
|
||||
JsonTextWriter writer = new JsonTextWriter(new ThrowingWriter(' '));
|
||||
writer.Formatting = Formatting.Indented;
|
||||
await writer.WriteStartObjectAsync();
|
||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(async () => await writer.WritePropertyNameAsync("aa"));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task FailureOnStartWriteObject()
|
||||
{
|
||||
JsonTextWriter writer = new JsonTextWriter(new ThrowingWriter('{'));
|
||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(async () => await writer.WriteStartObjectAsync());
|
||||
}
|
||||
|
||||
public class ThrowingWriter : TextWriter
|
||||
{
|
||||
// allergic to certain characters, this null-stream writer throws on any attempt to write them.
|
||||
|
||||
private char[] _singleCharBuffer = new char[1];
|
||||
|
||||
public ThrowingWriter(params char[] throwChars)
|
||||
{
|
||||
ThrowChars = throwChars;
|
||||
}
|
||||
|
||||
public char[] ThrowChars { get; set; }
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
public override Task WriteAsync(char value)
|
||||
{
|
||||
_singleCharBuffer[0] = value;
|
||||
return WriteAsync(_singleCharBuffer, 0, 1);
|
||||
}
|
||||
|
||||
public override Task WriteAsync(char[] buffer, int index, int count)
|
||||
{
|
||||
if (buffer.Skip(index).Take(count).Any(c => ThrowChars.Contains(c)))
|
||||
{
|
||||
// Pre-4.6 equivalent to .FromException()
|
||||
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
|
||||
tcs.SetException(new InvalidOperationException());
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
public override Task WriteAsync(string value)
|
||||
{
|
||||
return WriteAsync(value.ToCharArray(), 0, value.Length);
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomAsyncJsonTextWriter : CustomJsonTextWriter
|
||||
|
||||
@@ -186,9 +186,13 @@ namespace Newtonsoft.Json
|
||||
private Task WriteValueInternalAsync(JsonToken token, string value, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteValueAsync(token, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return _writer.WriteAsync(value, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return _writer.WriteAsync(value, cancellationToken);
|
||||
}
|
||||
|
||||
return WriteValueInternalAsync(task, value, cancellationToken);
|
||||
@@ -266,9 +270,13 @@ namespace Newtonsoft.Json
|
||||
private Task WriteIntegerValueAsync(ulong uvalue, bool negative, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteValueAsync(JsonToken.Integer, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteDigitsAsync(uvalue, negative, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return WriteDigitsAsync(uvalue, negative, cancellationToken);
|
||||
}
|
||||
|
||||
return WriteIntegerValueAsync(task, uvalue, negative, cancellationToken);
|
||||
@@ -317,18 +325,30 @@ namespace Newtonsoft.Json
|
||||
internal Task DoWritePropertyNameAsync(string name, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWritePropertyNameAsync(name, cancellationToken);
|
||||
if (!task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return DoWritePropertyNameAsync(task, name, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
// Completed synchronously (or very fast!), continue within this rather
|
||||
// that the default branch which uses async
|
||||
break;
|
||||
default:
|
||||
return DoWritePropertyNameAsync(task, name, cancellationToken);
|
||||
}
|
||||
|
||||
task = WriteEscapedStringAsync(name, _quoteName, cancellationToken);
|
||||
if (!task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return JavaScriptUtils.WriteCharAsync(task, _writer, ':', cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return _writer.WriteAsync(':', cancellationToken);
|
||||
}
|
||||
|
||||
return _writer.WriteAsync(':', cancellationToken);
|
||||
return JavaScriptUtils.WriteCharAsync(task, _writer, ':', cancellationToken);
|
||||
}
|
||||
|
||||
private async Task DoWritePropertyNameAsync(Task task, string name, CancellationToken cancellationToken)
|
||||
@@ -395,9 +415,13 @@ namespace Newtonsoft.Json
|
||||
internal Task DoWriteStartArrayAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteStartAsync(JsonToken.StartArray, JsonContainerType.Array, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return _writer.WriteAsync('[', cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return _writer.WriteAsync('[', cancellationToken);
|
||||
}
|
||||
|
||||
return DoWriteStartArrayAsync(task, cancellationToken);
|
||||
@@ -425,9 +449,13 @@ namespace Newtonsoft.Json
|
||||
internal Task DoWriteStartObjectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteStartAsync(JsonToken.StartObject, JsonContainerType.Object, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return _writer.WriteAsync('{', cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return _writer.WriteAsync('{', cancellationToken);
|
||||
}
|
||||
|
||||
return DoWriteStartObjectAsync(task, cancellationToken);
|
||||
@@ -477,9 +505,13 @@ namespace Newtonsoft.Json
|
||||
internal Task DoWriteUndefinedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteValueAsync(JsonToken.Undefined, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return _writer.WriteAsync(JsonConvert.Undefined, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return _writer.WriteAsync(JsonConvert.Undefined, cancellationToken);
|
||||
}
|
||||
|
||||
return DoWriteUndefinedAsync(task, cancellationToken);
|
||||
@@ -1054,9 +1086,13 @@ namespace Newtonsoft.Json
|
||||
internal Task DoWriteValueAsync(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteValueAsync(JsonToken.String, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return value == null ? _writer.WriteAsync(JsonConvert.Null, cancellationToken) : WriteEscapedStringAsync(value, true, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return value == null ? _writer.WriteAsync(JsonConvert.Null, cancellationToken) : WriteEscapedStringAsync(value, true, cancellationToken);
|
||||
}
|
||||
|
||||
return DoWriteValueAsync(task, value, cancellationToken);
|
||||
@@ -1189,9 +1225,13 @@ namespace Newtonsoft.Json
|
||||
internal Task WriteValueNotNullAsync(Uri value, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = InternalWriteValueAsync(JsonToken.String, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteEscapedStringAsync(value.OriginalString, true, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return WriteEscapedStringAsync(value.OriginalString, true, cancellationToken);
|
||||
}
|
||||
|
||||
return WriteValueNotNullAsync(task, value, cancellationToken);
|
||||
@@ -1310,9 +1350,13 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
UpdateScopeWithFinishedValue();
|
||||
Task task = AutoCompleteAsync(JsonToken.Undefined, cancellationToken);
|
||||
if (task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteRawAsync(json, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return WriteRawAsync(json, cancellationToken);
|
||||
}
|
||||
|
||||
return DoWriteRawValueAsync(task, json, cancellationToken);
|
||||
|
||||
@@ -44,12 +44,16 @@ namespace Newtonsoft.Json.Linq
|
||||
public override Task WriteToAsync(JsonWriter writer, CancellationToken cancellationToken, params JsonConverter[] converters)
|
||||
{
|
||||
Task task = writer.WritePropertyNameAsync(_name, cancellationToken);
|
||||
if (!task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteToAsync(task, writer, cancellationToken, converters);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return WriteValueAsync(writer, cancellationToken, converters);
|
||||
}
|
||||
|
||||
return WriteValueAsync(writer, cancellationToken, converters);
|
||||
return WriteToAsync(task, writer, cancellationToken, converters);
|
||||
}
|
||||
|
||||
private async Task WriteToAsync(Task task, JsonWriter writer, CancellationToken cancellationToken, params JsonConverter[] converters)
|
||||
|
||||
@@ -381,21 +381,34 @@ namespace Newtonsoft.Json.Utilities
|
||||
bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, JsonTextWriter client, char[] writeBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = writer.WriteAsync(delimiter, cancellationToken);
|
||||
if (!task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteEscapedJavaScriptStringWithDelimitersAsync(task, writer, s, delimiter, charEscapeFlags, stringEscapeHandling, client, writeBuffer, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
// Completed synchronously (or very fast!), continue within this rather
|
||||
// that the default branch which uses async
|
||||
break;
|
||||
default:
|
||||
return WriteEscapedJavaScriptStringWithDelimitersAsync(task, writer, s, delimiter, charEscapeFlags, stringEscapeHandling, client, writeBuffer, cancellationToken);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(s))
|
||||
{
|
||||
task = WriteEscapedJavaScriptStringWithoutDelimitersAsync(writer, s, charEscapeFlags, stringEscapeHandling, client, writeBuffer, cancellationToken);
|
||||
if (!task.IsCompleted)
|
||||
switch (task.Status)
|
||||
{
|
||||
return WriteCharAsync(task, writer, delimiter, cancellationToken);
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
return task;
|
||||
case TaskStatus.RanToCompletion:
|
||||
return writer.WriteAsync(delimiter, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
return writer.WriteAsync(delimiter, cancellationToken);
|
||||
return WriteCharAsync(task, writer, delimiter, cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
private static async Task WriteEscapedJavaScriptStringWithDelimitersAsync(Task task, TextWriter writer, string s, char delimiter,
|
||||
|
||||
Reference in New Issue
Block a user