Merge pull request #1159 from JonHanna/fault_on_fault
Don't eat exceptions from faulted Tasks.
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