@@ -198,7 +198,7 @@ public void WriteResponseHeaders(int streamId, int statusCode, Http2HeadersFrame
198
198
}
199
199
}
200
200
201
- public ValueTask < FlushResult > WriteResponseTrailers ( int streamId , HttpResponseTrailers headers )
201
+ public ValueTask < FlushResult > WriteResponseTrailersAsync ( int streamId , HttpResponseTrailers headers )
202
202
{
203
203
lock ( _writeLock )
204
204
{
@@ -256,6 +256,9 @@ private void FinishWritingHeaders(int streamId, int payloadLength, bool done)
256
256
257
257
public ValueTask < FlushResult > WriteDataAsync ( int streamId , StreamOutputFlowControl flowControl , in ReadOnlySequence < byte > data , bool endStream , bool firstWrite , bool forceFlush )
258
258
{
259
+ // Logic in this method is replicated in WriteDataAndTrailersAsync.
260
+ // Changes here may need to be mirrored in WriteDataAndTrailersAsync.
261
+
259
262
// The Length property of a ReadOnlySequence can be expensive, so we cache the value.
260
263
var dataLength = data . Length ;
261
264
@@ -286,6 +289,43 @@ public ValueTask<FlushResult> WriteDataAsync(int streamId, StreamOutputFlowContr
286
289
}
287
290
}
288
291
292
+ public ValueTask < FlushResult > WriteDataAndTrailersAsync ( int streamId , StreamOutputFlowControl flowControl , in ReadOnlySequence < byte > data , bool firstWrite , HttpResponseTrailers headers )
293
+ {
294
+ // This method combines WriteDataAsync and WriteResponseTrailers.
295
+ // Changes here may need to be mirrored in WriteDataAsync.
296
+
297
+ // The Length property of a ReadOnlySequence can be expensive, so we cache the value.
298
+ var dataLength = data . Length ;
299
+
300
+ lock ( _writeLock )
301
+ {
302
+ if ( _completed || flowControl . IsAborted )
303
+ {
304
+ return default ;
305
+ }
306
+
307
+ // Zero-length data frames are allowed to be sent immediately even if there is no space available in the flow control window.
308
+ // https://httpwg.org/specs/rfc7540.html#rfc.section.6.9.1
309
+ if ( dataLength != 0 && dataLength > flowControl . Available )
310
+ {
311
+ return WriteDataAndTrailersAsyncCore ( this , streamId , flowControl , data , dataLength , firstWrite , headers ) ;
312
+ }
313
+
314
+ // This cast is safe since if dataLength would overflow an int, it's guaranteed to be greater than the available flow control window.
315
+ flowControl . Advance ( ( int ) dataLength ) ;
316
+ WriteDataUnsynchronized ( streamId , data , dataLength , endStream : false ) ;
317
+
318
+ return WriteResponseTrailersAsync ( streamId , headers ) ;
319
+ }
320
+
321
+ static async ValueTask < FlushResult > WriteDataAndTrailersAsyncCore ( Http2FrameWriter writer , int streamId , StreamOutputFlowControl flowControl , ReadOnlySequence < byte > data , long dataLength , bool firstWrite , HttpResponseTrailers headers )
322
+ {
323
+ await writer . WriteDataAsync ( streamId , flowControl , data , dataLength , endStream : false , firstWrite ) ;
324
+
325
+ return await writer . WriteResponseTrailersAsync ( streamId , headers ) ;
326
+ }
327
+ }
328
+
289
329
/* Padding is not implemented
290
330
+---------------+
291
331
|Pad Length? (8)|
0 commit comments