Merge pull request #1159 from JonHanna/fault_on_fault

Don't eat exceptions from faulted Tasks.
This commit is contained in:
James Newton-King
2017-01-11 09:30:42 +13:00
committed by GitHub
4 changed files with 152 additions and 29 deletions
@@ -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
+65 -21
View File
@@ -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);
+7 -3
View File
@@ -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,