2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System . Diagnostics . CodeAnalysis ;
5
+ using System . IO . Pipelines ;
5
6
using System . Text . Json ;
6
7
using System . Text . Json . Serialization ;
7
8
using System . Text . Json . Serialization . Metadata ;
@@ -89,13 +90,23 @@ public static Task WriteAsJsonAsync<TValue>(
89
90
90
91
response . ContentType = contentType ?? JsonConstants . JsonContentTypeWithCharset ;
91
92
93
+ var startTask = Task . CompletedTask ;
94
+ if ( ! response . HasStarted )
95
+ {
96
+ // Flush headers before starting Json serialization. This avoids an extra layer of buffering before the first flush.
97
+ startTask = response . StartAsync ( cancellationToken ) ;
98
+ }
99
+
92
100
// if no user provided token, pass the RequestAborted token and ignore OperationCanceledException
93
- if ( ! cancellationToken . CanBeCanceled )
101
+ if ( ! startTask . IsCompleted || ! cancellationToken . CanBeCanceled )
94
102
{
95
- return WriteAsJsonAsyncSlow ( response . Body , value , options , response . HttpContext . RequestAborted ) ;
103
+ return WriteAsJsonAsyncSlow ( startTask , response . BodyWriter , value , options ,
104
+ ignoreOCE : ! cancellationToken . CanBeCanceled ,
105
+ cancellationToken . CanBeCanceled ? cancellationToken : response . HttpContext . RequestAborted ) ;
96
106
}
97
107
98
- return JsonSerializer . SerializeAsync ( response . Body , value , options , cancellationToken ) ;
108
+ startTask . GetAwaiter ( ) . GetResult ( ) ;
109
+ return JsonSerializer . SerializeAsync ( response . BodyWriter , value , options , cancellationToken ) ;
99
110
}
100
111
101
112
/// <summary>
@@ -120,21 +131,33 @@ public static Task WriteAsJsonAsync<TValue>(
120
131
121
132
response . ContentType = contentType ?? JsonConstants . JsonContentTypeWithCharset ;
122
133
134
+ var startTask = Task . CompletedTask ;
135
+ if ( ! response . HasStarted )
136
+ {
137
+ // Flush headers before starting Json serialization. This avoids an extra layer of buffering before the first flush.
138
+ startTask = response . StartAsync ( cancellationToken ) ;
139
+ }
140
+
123
141
// if no user provided token, pass the RequestAborted token and ignore OperationCanceledException
124
- if ( ! cancellationToken . CanBeCanceled )
142
+ if ( ! startTask . IsCompleted || ! cancellationToken . CanBeCanceled )
125
143
{
126
- return WriteAsJsonAsyncSlow ( response , value , jsonTypeInfo ) ;
144
+ return WriteAsJsonAsyncSlow ( startTask , response , value , jsonTypeInfo ,
145
+ ignoreOCE : ! cancellationToken . CanBeCanceled ,
146
+ cancellationToken . CanBeCanceled ? cancellationToken : response . HttpContext . RequestAborted ) ;
127
147
}
128
148
129
- return JsonSerializer . SerializeAsync ( response . Body , value , jsonTypeInfo , cancellationToken ) ;
149
+ startTask . GetAwaiter ( ) . GetResult ( ) ;
150
+ return JsonSerializer . SerializeAsync ( response . BodyWriter , value , jsonTypeInfo , cancellationToken ) ;
130
151
131
- static async Task WriteAsJsonAsyncSlow ( HttpResponse response , TValue value , JsonTypeInfo < TValue > jsonTypeInfo )
152
+ static async Task WriteAsJsonAsyncSlow ( Task startTask , HttpResponse response , TValue value , JsonTypeInfo < TValue > jsonTypeInfo ,
153
+ bool ignoreOCE , CancellationToken cancellationToken )
132
154
{
133
155
try
134
156
{
135
- await JsonSerializer . SerializeAsync ( response . Body , value , jsonTypeInfo , response . HttpContext . RequestAborted ) ;
157
+ await startTask ;
158
+ await JsonSerializer . SerializeAsync ( response . BodyWriter , value , jsonTypeInfo , cancellationToken ) ;
136
159
}
137
- catch ( OperationCanceledException ) { }
160
+ catch ( OperationCanceledException ) when ( ignoreOCE ) { }
138
161
}
139
162
}
140
163
@@ -161,37 +184,52 @@ public static Task WriteAsJsonAsync(
161
184
162
185
response . ContentType = contentType ?? JsonConstants . JsonContentTypeWithCharset ;
163
186
187
+ var startTask = Task . CompletedTask ;
188
+ if ( ! response . HasStarted )
189
+ {
190
+ // Flush headers before starting Json serialization. This avoids an extra layer of buffering before the first flush.
191
+ startTask = response . StartAsync ( cancellationToken ) ;
192
+ }
193
+
164
194
// if no user provided token, pass the RequestAborted token and ignore OperationCanceledException
165
- if ( ! cancellationToken . CanBeCanceled )
195
+ if ( ! startTask . IsCompleted || ! cancellationToken . CanBeCanceled )
166
196
{
167
- return WriteAsJsonAsyncSlow ( response , value , jsonTypeInfo ) ;
197
+ return WriteAsJsonAsyncSlow ( startTask , response , value , jsonTypeInfo ,
198
+ ignoreOCE : ! cancellationToken . CanBeCanceled ,
199
+ cancellationToken . CanBeCanceled ? cancellationToken : response . HttpContext . RequestAborted ) ;
168
200
}
169
201
170
- return JsonSerializer . SerializeAsync ( response . Body , value , jsonTypeInfo , cancellationToken ) ;
202
+ startTask . GetAwaiter ( ) . GetResult ( ) ;
203
+ return JsonSerializer . SerializeAsync ( response . BodyWriter , value , jsonTypeInfo , cancellationToken ) ;
171
204
172
- static async Task WriteAsJsonAsyncSlow ( HttpResponse response , object ? value , JsonTypeInfo jsonTypeInfo )
205
+ static async Task WriteAsJsonAsyncSlow ( Task startTask , HttpResponse response , object ? value , JsonTypeInfo jsonTypeInfo ,
206
+ bool ignoreOCE , CancellationToken cancellationToken )
173
207
{
174
208
try
175
209
{
176
- await JsonSerializer . SerializeAsync ( response . Body , value , jsonTypeInfo , response . HttpContext . RequestAborted ) ;
210
+ await startTask ;
211
+ await JsonSerializer . SerializeAsync ( response . BodyWriter , value , jsonTypeInfo , cancellationToken ) ;
177
212
}
178
- catch ( OperationCanceledException ) { }
213
+ catch ( OperationCanceledException ) when ( ignoreOCE ) { }
179
214
}
180
215
}
181
216
182
217
[ RequiresUnreferencedCode ( RequiresUnreferencedCodeMessage ) ]
183
218
[ RequiresDynamicCode ( RequiresDynamicCodeMessage ) ]
184
219
private static async Task WriteAsJsonAsyncSlow < TValue > (
185
- Stream body ,
220
+ Task startTask ,
221
+ PipeWriter body ,
186
222
TValue value ,
187
223
JsonSerializerOptions ? options ,
224
+ bool ignoreOCE ,
188
225
CancellationToken cancellationToken )
189
226
{
190
227
try
191
228
{
229
+ await startTask ;
192
230
await JsonSerializer . SerializeAsync ( body , value , options , cancellationToken ) ;
193
231
}
194
- catch ( OperationCanceledException ) { }
232
+ catch ( OperationCanceledException ) when ( ignoreOCE ) { }
195
233
}
196
234
197
235
/// <summary>
@@ -266,29 +304,42 @@ public static Task WriteAsJsonAsync(
266
304
267
305
response . ContentType = contentType ?? JsonConstants . JsonContentTypeWithCharset ;
268
306
307
+ var startTask = Task . CompletedTask ;
308
+ if ( ! response . HasStarted )
309
+ {
310
+ // Flush headers before starting Json serialization. This avoids an extra layer of buffering before the first flush.
311
+ startTask = response . StartAsync ( cancellationToken ) ;
312
+ }
313
+
269
314
// if no user provided token, pass the RequestAborted token and ignore OperationCanceledException
270
- if ( ! cancellationToken . CanBeCanceled )
315
+ if ( ! startTask . IsCompleted || ! cancellationToken . CanBeCanceled )
271
316
{
272
- return WriteAsJsonAsyncSlow ( response . Body , value , type , options , response . HttpContext . RequestAborted ) ;
317
+ return WriteAsJsonAsyncSlow ( startTask , response . BodyWriter , value , type , options ,
318
+ ignoreOCE : ! cancellationToken . CanBeCanceled ,
319
+ cancellationToken . CanBeCanceled ? cancellationToken : response . HttpContext . RequestAborted ) ;
273
320
}
274
321
275
- return JsonSerializer . SerializeAsync ( response . Body , value , type , options , cancellationToken ) ;
322
+ startTask . GetAwaiter ( ) . GetResult ( ) ;
323
+ return JsonSerializer . SerializeAsync ( response . BodyWriter , value , type , options , cancellationToken ) ;
276
324
}
277
325
278
326
[ RequiresUnreferencedCode ( RequiresUnreferencedCodeMessage ) ]
279
327
[ RequiresDynamicCode ( RequiresDynamicCodeMessage ) ]
280
328
private static async Task WriteAsJsonAsyncSlow (
281
- Stream body ,
329
+ Task startTask ,
330
+ PipeWriter body ,
282
331
object ? value ,
283
332
Type type ,
284
333
JsonSerializerOptions ? options ,
334
+ bool ignoreOCE ,
285
335
CancellationToken cancellationToken )
286
336
{
287
337
try
288
338
{
339
+ await startTask ;
289
340
await JsonSerializer . SerializeAsync ( body , value , type , options , cancellationToken ) ;
290
341
}
291
- catch ( OperationCanceledException ) { }
342
+ catch ( OperationCanceledException ) when ( ignoreOCE ) { }
292
343
}
293
344
294
345
/// <summary>
@@ -316,21 +367,33 @@ public static Task WriteAsJsonAsync(
316
367
317
368
response . ContentType = contentType ?? JsonConstants . JsonContentTypeWithCharset ;
318
369
370
+ var startTask = Task . CompletedTask ;
371
+ if ( ! response . HasStarted )
372
+ {
373
+ // Flush headers before starting Json serialization. This avoids an extra layer of buffering before the first flush.
374
+ startTask = response . StartAsync ( cancellationToken ) ;
375
+ }
376
+
319
377
// if no user provided token, pass the RequestAborted token and ignore OperationCanceledException
320
- if ( ! cancellationToken . CanBeCanceled )
378
+ if ( ! startTask . IsCompleted || ! cancellationToken . CanBeCanceled )
321
379
{
322
- return WriteAsJsonAsyncSlow ( ) ;
380
+ return WriteAsJsonAsyncSlow ( startTask , response . BodyWriter , value , type , context ,
381
+ ignoreOCE : ! cancellationToken . CanBeCanceled ,
382
+ cancellationToken . CanBeCanceled ? cancellationToken : response . HttpContext . RequestAborted ) ;
323
383
}
324
384
325
- return JsonSerializer . SerializeAsync ( response . Body , value , type , context , cancellationToken ) ;
385
+ startTask . GetAwaiter ( ) . GetResult ( ) ;
386
+ return JsonSerializer . SerializeAsync ( response . BodyWriter , value , type , context , cancellationToken ) ;
326
387
327
- async Task WriteAsJsonAsyncSlow ( )
388
+ static async Task WriteAsJsonAsyncSlow ( Task startTask , PipeWriter body , object ? value , Type type , JsonSerializerContext context ,
389
+ bool ignoreOCE , CancellationToken cancellationToken )
328
390
{
329
391
try
330
392
{
331
- await JsonSerializer . SerializeAsync ( response . Body , value , type , context , cancellationToken ) ;
393
+ await startTask ;
394
+ await JsonSerializer . SerializeAsync ( body , value , type , context , cancellationToken ) ;
332
395
}
333
- catch ( OperationCanceledException ) { }
396
+ catch ( OperationCanceledException ) when ( ignoreOCE ) { }
334
397
}
335
398
}
336
399
0 commit comments