// Construct and initialize the new Http2Stream instance. It's stream ID must be set below
// before the instance is used and stored into the dictionary. However, we construct it here
// so as to avoid the allocation and initialization expense while holding multiple locks.
- var http2Stream = new Http2Stream(request, this, _initialWindowSize);
+ var http2Stream = new Http2Stream(request, this);
// Start the write. This serializes access to write to the connection, and ensures that HEADERS
// and CONTINUATION frames stay together, as they must do. We use the lock as well to ensure new
s.thisRef.ThrowShutdownException();
}
+ // Now that we're holding the lock, configure the stream. The lock must be held while
+ // assigning the stream ID to ensure only one stream gets an ID, and it must be held
+ // across setting the initial window size (available credit) and storing the stream into
+ // collection such that window size updates are able to atomically affect all known streams.
+ s.http2Stream.Initialize(s.thisRef._nextStream, _initialWindowSize);
+
// Client-initiated streams are always odd-numbered, so increase by 2.
- s.http2Stream.StreamId = s.thisRef._nextStream;
s.thisRef._nextStream += 2;
// We're about to flush the HEADERS frame, so add the stream to the dictionary now.
// See comment on ConnectionWindowThreshold.
private const int StreamWindowThreshold = StreamWindowSize / 8;
- public Http2Stream(HttpRequestMessage request, Http2Connection connection, int initialWindowSize)
+ public Http2Stream(HttpRequestMessage request, Http2Connection connection)
{
_request = request;
_connection = connection;
_responseBuffer = new ArrayBuffer(InitialStreamBufferSize, usePool: true);
_pendingWindowUpdate = 0;
- _availableCredit = initialWindowSize;
-
_headerBudgetRemaining = connection._pool.Settings._maxResponseHeadersLength * 1024;
if (_request.Content == null)
RequestMessage = _request,
Content = new HttpConnectionResponseContent()
};
-
- if (NetEventSource.IsEnabled) Trace($"{request}, {nameof(initialWindowSize)}={initialWindowSize}");
}
private object SyncObject => this; // this isn't handed out to code that may lock on it
- public int StreamId { get; set; }
+ public void Initialize(int streamId, int initialWindowSize)
+ {
+ StreamId = streamId;
+ _availableCredit = initialWindowSize;
+ if (NetEventSource.IsEnabled) Trace($"{_request}, {nameof(initialWindowSize)}={initialWindowSize}");
+ }
+
+ public int StreamId { get; private set; }
public HttpResponseMessage GetAndClearResponse()
{