diff --git a/.build/Build.Environment.cs b/.build/Build.Environment.cs index a210e60fd65..afa19384e77 100644 --- a/.build/Build.Environment.cs +++ b/.build/Build.Environment.cs @@ -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"; } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerExtensions.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerExtensions.cs index a1b1637e2dc..d6b472397ca 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerExtensions.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerExtensions.cs @@ -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; diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpMultipartMiddlewareTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpMultipartMiddlewareTests.cs index 2f01cf1806f..c24e44244c0 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpMultipartMiddlewareTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpMultipartMiddlewareTests.cs @@ -210,9 +210,9 @@ public async Task Upload_File() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "upload", null } + { "upload", null } } }); @@ -245,9 +245,9 @@ public async Task Upload_Optional_File() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "upload", null } + { "upload", null } } }); @@ -280,9 +280,9 @@ public async Task Upload_Optional_File_In_InputObject() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "input", new Dictionary { { "file", null } } } + { "input", new Dictionary { { "file", null } } } } }); @@ -315,9 +315,9 @@ public async Task Upload_Optional_File_In_Inline_InputObject() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "upload", null } + { "upload", null } } }); @@ -350,9 +350,9 @@ public async Task Upload_File_In_InputObject() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "input", new Dictionary { { "file", null } } } + { "input", new Dictionary { { "file", null } } } } }); @@ -385,9 +385,9 @@ public async Task Upload_File_Inline_InputObject() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "upload", null } + { "upload", null } } }); @@ -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 + Variables = new Dictionary { + { + "input", + new List { - "input", new List { - new List - { - new Dictionary { { "file", null } } - } + new Dictionary { { "file", null } } } } + } } }); @@ -464,9 +464,9 @@ public async Task Upload_Too_Large_File_Test() new ClientQueryRequest { Query = query, - Variables = new Dictionary + Variables = new Dictionary { - { "upload", null } + { "upload", null } } }); diff --git a/src/HotChocolate/Core/src/Execution/Serialization/MultiPartResponseStreamFormatter.cs b/src/HotChocolate/Core/src/Execution/Serialization/MultiPartResponseStreamFormatter.cs index 7766bd24ce9..75805b5dbeb 100644 --- a/src/HotChocolate/Core/src/Execution/Serialization/MultiPartResponseStreamFormatter.cs +++ b/src/HotChocolate/Core/src/Execution/Serialization/MultiPartResponseStreamFormatter.cs @@ -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); @@ -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); } @@ -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); } @@ -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); } @@ -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); }