Skip to content

Commit

Permalink
Optimized Deferred Result Streams
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Sep 12, 2022
1 parent d1bfcd7 commit 44d68d5
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .build/Build.Environment.cs
Expand Up @@ -29,5 +29,5 @@ partial class Build
AbsolutePath TemplatesNuSpec => RootDirectory / "templates" / "v12" / "HotChocolate.Templates.nuspec";
AbsolutePath EmptyServer12Proj => RootDirectory / "templates" / "v12" / "server" / "HotChocolate.Template.Server.csproj";
AbsolutePath EmptyAzf12Proj => RootDirectory / "templates" / "v12" / "function" / "HotChocolate.Template.AzureFunctions.csproj";
AbsolutePath EmptyAzfUp12Proj => RootDirectory / "templates" / "v12" / "function-isolated" / "HotChocolate.Template.AzureFunctions.csproj";
AbsolutePath EmptyAzfUp12Proj => RootDirectory / "templates" / "v12" / "function-isolated" / "HotChocolate.Template.AzureFunctions.Isolated.csproj";
}
@@ -1,6 +1,5 @@
using System.Net;
using System.Text;
using HotChocolate.Execution.Processing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.WebUtilities;
using Newtonsoft.Json;
Expand Down
Expand Up @@ -210,9 +210,9 @@ public async Task Upload_File()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "upload", null }
{ "upload", null }
}
});

Expand Down Expand Up @@ -245,9 +245,9 @@ public async Task Upload_Optional_File()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "upload", null }
{ "upload", null }
}
});

Expand Down Expand Up @@ -280,9 +280,9 @@ public async Task Upload_Optional_File_In_InputObject()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "input", new Dictionary<string, object> { { "file", null } } }
{ "input", new Dictionary<string, object?> { { "file", null } } }
}
});

Expand Down Expand Up @@ -315,9 +315,9 @@ public async Task Upload_Optional_File_In_Inline_InputObject()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "upload", null }
{ "upload", null }
}
});

Expand Down Expand Up @@ -350,9 +350,9 @@ public async Task Upload_File_In_InputObject()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "input", new Dictionary<string, object> { { "file", null } } }
{ "input", new Dictionary<string, object?> { { "file", null } } }
}
});

Expand Down Expand Up @@ -385,9 +385,9 @@ public async Task Upload_File_Inline_InputObject()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "upload", null }
{ "upload", null }
}
});

Expand All @@ -411,27 +411,27 @@ public async Task Upload_File_In_List()
// arrange
var server = CreateStarWarsServer();

var query = @"
query ($input: [[InputWithFileInput!]!]!) {
listUpload(input: $input)
}";
const string query =
@"query ($input: [[InputWithFileInput!]!]!) {
listUpload(input: $input)
}";

var request = JsonConvert.SerializeObject(
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{
"input",
new List<object>
{
"input",
new List<object>
{
new List<object>
{
new Dictionary<string, object> { { "file", null } }
}
new Dictionary<string, object?> { { "file", null } }
}
}
}
}
});

Expand Down Expand Up @@ -464,9 +464,9 @@ public async Task Upload_Too_Large_File_Test()
new ClientQueryRequest
{
Query = query,
Variables = new Dictionary<string, object>
Variables = new Dictionary<string, object?>
{
{ "upload", null }
{ "upload", null }
}
});

Expand Down
Expand Up @@ -127,23 +127,25 @@ public sealed partial class MultiPartResponseStreamFormatter : IExecutionResultF
{
// first we create the iterator.
await using var enumerator = responseStream.ReadResultsAsync().GetAsyncEnumerator(ct);

// next we write a leading CRLF
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
var first = true;

while (await enumerator.MoveNextAsync().ConfigureAwait(false))
{
try
{
// Before each part of the multi-part response, a boundary (---, CRLF)
// is sent.
await WriteNextAsync(outputStream, ct).ConfigureAwait(false);
if (first || responseStream.Kind is not DeferredResult)
{
await WriteNextAsync(outputStream, ct).ConfigureAwait(false);
first = false;
}

// Now we can write the header and body of the part.
await WriteResultAsync(enumerator.Current, outputStream, ct).ConfigureAwait(false);

// after each result we write a CRLF signaling the next or final part.
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
if (responseStream.Kind is DeferredResult && (enumerator.Current.HasNext ?? false))
{
await WriteNextAsync(outputStream, ct).ConfigureAwait(false);
}

// we flush to make sure that the result is written to the network stream.
await outputStream.FlushAsync(ct).ConfigureAwait(false);
Expand All @@ -156,8 +158,7 @@ public sealed partial class MultiPartResponseStreamFormatter : IExecutionResultF
}
}

// After the final payload, the terminating boundary of
// ----- followed by CRLF is sent.

await WriteEndAsync(outputStream, ct).ConfigureAwait(false);
await outputStream.FlushAsync(ct).ConfigureAwait(false);
}
Expand Down Expand Up @@ -189,8 +190,6 @@ public sealed partial class MultiPartResponseStreamFormatter : IExecutionResultF
await queryResult.DisposeAsync().ConfigureAwait(false);
}

// After the final payload, the terminating boundary of
// ----- followed by CRLF is sent.
await WriteEndAsync(outputStream, ct).ConfigureAwait(false);
await outputStream.FlushAsync(ct).ConfigureAwait(false);
}
Expand Down Expand Up @@ -229,6 +228,9 @@ public sealed partial class MultiPartResponseStreamFormatter : IExecutionResultF
Stream outputStream,
CancellationToken ct)
{
// Before each part of the multi-part response, a boundary (CRLF, ---, CRLF)
// is sent.
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
await outputStream.WriteAsync(Start, 0, Start.Length, ct).ConfigureAwait(false);
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
}
Expand All @@ -237,6 +239,9 @@ public sealed partial class MultiPartResponseStreamFormatter : IExecutionResultF
Stream outputStream,
CancellationToken ct)
{
// After the final payload, the terminating boundary of
// CRLF, ----- followed by CRLF is sent.
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
await outputStream.WriteAsync(End, 0, End.Length, ct).ConfigureAwait(false);
await outputStream.WriteAsync(CrLf, 0, CrLf.Length, ct).ConfigureAwait(false);
}
Expand Down

0 comments on commit 44d68d5

Please sign in to comment.