public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers) { lock (_writeLock) { _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); var done = _hpackEncoder.BeginEncode(statusCode, EnumerateHeaders(headers), _outgoingFrame.Payload, out var payloadLength); _outgoingFrame.Length = payloadLength; if (done) { _outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS; } Append(_outgoingFrame.Raw); while (!done) { _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); done = _hpackEncoder.Encode(_outgoingFrame.Payload, out var length); _outgoingFrame.Length = length; if (done) { _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; } Append(_outgoingFrame.Raw); } } }
private void FinishWritingHeaders(int streamId, int payloadLength, bool done) { var buffer = _headerEncodingBuffer.AsSpan(); _outgoingFrame.PayloadLength = payloadLength; if (done) { _outgoingFrame.HeadersFlags |= Http2HeadersFrameFlags.END_HEADERS; } WriteHeaderUnsynchronized(); _outputWriter.Write(buffer.Slice(0, payloadLength)); while (!done) { _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); done = _hpackEncoder.Encode(buffer, out payloadLength); _outgoingFrame.PayloadLength = payloadLength; if (done) { _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; } WriteHeaderUnsynchronized(); _outputWriter.Write(buffer.Slice(0, payloadLength)); } }
// Optional header fields for padding and priority are not implemented. /* https://tools.ietf.org/html/rfc7540#section-6.2 +---------------+ |Pad Length? (8)| +-+-------------+-----------------------------------------------+ |E| Stream Dependency? (31) | +-+-------------+-----------------------------------------------+ | Weight? (8) | +-+-------------+-----------------------------------------------+ | Header Block Fragment (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+ */ public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers) { lock (_writeLock) { if (_completed) { return; } try { _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); var buffer = _headerEncodingBuffer.AsSpan(); var done = _hpackEncoder.BeginEncode(statusCode, EnumerateHeaders(headers), buffer, out var payloadLength); _outgoingFrame.PayloadLength = payloadLength; if (done) { _outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS; } WriteHeaderUnsynchronized(); _outputWriter.Write(buffer.Slice(0, payloadLength)); while (!done) { _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); done = _hpackEncoder.Encode(buffer, out payloadLength); _outgoingFrame.PayloadLength = payloadLength; if (done) { _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; } WriteHeaderUnsynchronized(); _outputWriter.Write(buffer.Slice(0, payloadLength)); } } catch (HPackEncodingException hex) { // Header errors are fatal to the connection. We don't have a direct way to signal this to the Http2Connection. _connectionContext.Abort(new ConnectionAbortedException("", hex)); throw new InvalidOperationException("", hex); // Report the error to the user if this was the first write. } } }