public void WriteHeadersFrame(HeadersList headers, bool isEndStream) { Headers = headers; byte[] headerBytes = _compressionProc.Compress(headers); var frame = new HeadersFrame(_id, headerBytes, Priority) { IsEndHeaders = true, IsEndStream = isEndStream, }; if (frame.IsEndStream) { EndStreamSent = true; } _writeQueue.WriteFrame(frame); if (OnFrameSent != null) { OnFrameSent(this, new FrameSentArgs(frame)); } }
public void PumpToStream(CancellationToken cancel) { if (cancel == null) { throw new ArgumentNullException("cancellation token is null"); } while (!_disposed) { if (cancel.IsCancellationRequested) { cancel.ThrowIfCancellationRequested(); } // send one at a time lock (_writeLock) { if (_messageQueue.Count > 0) { var entry = _messageQueue.Dequeue(); /* see https://github.com/MSOpenTech/http2-katana/issues/55 * It's critically important to keep compression context before sending * headers frame. Since that we are unable to construct headers frame with * compressed headers block as part of the frame's Buffer, because Queue has * prioritization mechanism and we must compress headers list immediately before * sending it. */ if (IsPriorityTurnedOn && entry.Frame is IHeadersFrame && entry.Frame is IPaddingFrame) { /* There are two frame types bears Headers Block Fragment: HEADERS and PUSH_PROMISE * and CONTINUATION, which implements IHeadersFrame interface. It can include additional * padding as well. Since that we call to interface methods to avoid code redundant. */ // frame reconstruction: headers compression var headers = (entry.Frame as IHeadersFrame).Headers; var compressedHeaders = _proc.Compress(headers); entry.Frame.PayloadLength += compressedHeaders.Length; // frame reconstruction: add padding var paddingFrame = entry.Frame as IPaddingFrame; byte[] padding = new byte[paddingFrame.PadHigh * 256 + paddingFrame.PadLow]; entry.Frame.PayloadLength += padding.Length; if (entry.Frame is HeadersFrame) { var headersFrame = entry.Frame as HeadersFrame; Http2Logger.LogDebug("Sending HEADERS frame: stream id={0}, payload len={1}, " + "has pad={2}, pad high={3}, pad low={4}, end stream={5}, " + "has priority={6}, exclusive={7}, dependency={8}, weight={9}", headersFrame.StreamId, headersFrame.PayloadLength, headersFrame.HasPadding, headersFrame.PadHigh, headersFrame.PadLow, headersFrame.IsEndStream, headersFrame.HasPriority, headersFrame.Exclusive, headersFrame.StreamDependency, headersFrame.Weight); } if (entry.Frame is PushPromiseFrame) { var pushPromiseFrame = entry.Frame as PushPromiseFrame; Http2Logger.LogDebug("Sending PUSH_PROMISE frame: stream id={0}, payload len={1}, " + "promised id={2}, has pad={3}, pad high={4}, pad low={5}, end headers={6}", pushPromiseFrame.StreamId, pushPromiseFrame.PayloadLength, pushPromiseFrame.PromisedStreamId, pushPromiseFrame.HasPadding, pushPromiseFrame.PadHigh, pushPromiseFrame.PadLow, pushPromiseFrame.IsEndHeaders); } if (entry.Frame is ContinuationFrame) { var contFrame = entry.Frame as ContinuationFrame; Http2Logger.LogDebug("Sending CONTINUATION frame: stream id={0}, payload len={1}, has pad={2}, " + "pad high={3}, pad low={4}, end headers={5}", contFrame.StreamId, contFrame.PayloadLength, contFrame.HasPadding, contFrame.PadHigh, contFrame.PadLow, contFrame.IsEndHeaders); } // write frame preamble _stream.Write(entry.Buffer, 0, entry.Buffer.Length); // write compressed Headers Block _stream.Write(compressedHeaders, 0, compressedHeaders.Length); // write frame padding _stream.Write(padding, 0, padding.Length); } else { _stream.Write(entry.Buffer, 0, entry.Buffer.Length); } } else { Thread.Sleep(10); } } _stream.Flush(); } }