// This will automatically handle the control stream, including validating its contents public async Task <Http3LoopbackStream> AcceptRequestStreamAsync() { Http3LoopbackStream requestStream = null; while (true) { var stream = await AcceptStreamAsync().ConfigureAwait(false); // Accepted request stream. if (stream.CanWrite) { // Only one expected. Assert.True(requestStream is null, "Expected single request stream, got a second"); // Control stream is set --> return the request stream. if (_inboundControlStream is not null) { return(stream); } // Control stream not set --> need to accept another stream. requestStream = stream; continue; } // Must be the control stream. await HandleControlStreamAsync(stream); // We've already accepted request stream --> return it. if (requestStream is not null) { return(requestStream); } } }
public async Task EstablishControlStreamAsync() { _outboundControlStream = await OpenUnidirectionalStreamAsync(); await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream); await _outboundControlStream.SendSettingsFrameAsync(); }
public async Task <(Http3LoopbackStream clientControlStream, Http3LoopbackStream requestStream)> AcceptControlAndRequestStreamAsync() { Http3LoopbackStream requestStream = await AcceptRequestStreamAsync(); Http3LoopbackStream controlStream = _inboundControlStream; return(controlStream, requestStream); }
public async Task <Http3LoopbackStream> AcceptStreamAsync() { QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false); var stream = new Http3LoopbackStream(quicStream); _openStreams.Add(checked ((int)quicStream.StreamId), stream); _currentStream = stream; return(stream); }
public override async Task SendResponseBodyAsync(byte[] content, bool isFinal = true, int requestId = 0) { Http3LoopbackStream stream = GetOpenRequest(requestId); if (content?.Length != 0) { await stream.SendDataFrameAsync(content).ConfigureAwait(false); } if (isFinal) { stream.ShutdownSend(); stream.Dispose(); } }
public async Task <(Http3LoopbackStream clientControlStream, Http3LoopbackStream requestStream)> AcceptControlAndRequestStreamAsync() { Http3LoopbackStream streamA = null, streamB = null; try { streamA = await AcceptStreamAsync(); streamB = await AcceptStreamAsync(); return((streamA.CanWrite, streamB.CanWrite) switch { (false, true) => (streamA, streamB), (true, false) => (streamB, streamA), _ => throw new Exception("Expected one unidirectional and one bidirectional stream; received something else.") }); }
public override async Task <HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList <HttpHeaderData> headers = null, string content = "") { Http3LoopbackStream stream = await AcceptRequestStreamAsync().ConfigureAwait(false); HttpRequestData request = await stream.ReadRequestDataAsync().ConfigureAwait(false); // We are about to close the connection, after we send the response. // So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection. // Note that in HTTP3 (unlike HTTP2) there is no strict ordering between the GOAWAY and the response below; // so the client may race in processing them and we need to handle this. await _outboundControlStream.SendGoAwayFrameAsync(stream.StreamId + 4); await stream.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false); await WaitForClientDisconnectAsync(); return(request); }
// This will automatically handle the control stream, including validating its contents public async Task <Http3LoopbackStream> AcceptRequestStreamAsync() { await EnsureControlStreamAcceptedAsync().ConfigureAwait(false); if (!_delayedStreams.TryDequeue(out QuicStream quicStream)) { quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false); } var stream = new Http3LoopbackStream(quicStream); Assert.True(quicStream.CanWrite, "Expected writeable stream."); _openStreams.Add(checked ((int)quicStream.Id), stream); _currentStream = stream; _currentStreamId = quicStream.Id; return(stream); }
private Task EnsureControlStreamAcceptedAsync() { if (_inboundControlStream != null) { return(Task.CompletedTask); } return(EnsureControlStreamAcceptedInternalAsync()); async Task EnsureControlStreamAcceptedInternalAsync() { Http3LoopbackStream controlStream; while (true) { QuicStream quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false); if (!quicStream.CanWrite) { // control stream accepted controlStream = new Http3LoopbackStream(quicStream); break; } // control streams are unidirectional, so this must be a request stream // keep it for later and wait for another stream _delayedStreams.Enqueue(quicStream); } long?streamType = await controlStream.ReadIntegerAsync(); Assert.Equal(Http3LoopbackStream.ControlStream, streamType); List <(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync(); (long settingId, long settingValue) = Assert.Single(settings); Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId); MaxHeaderListSize = settingValue; _inboundControlStream = controlStream; } }
private async Task HandleControlStreamAsync(Http3LoopbackStream controlStream) { if (_inboundControlStream is not null) { throw new Exception("Received second control stream from client???"); } long?streamType = await controlStream.ReadIntegerAsync(); Assert.Equal(Http3LoopbackStream.ControlStream, streamType); List <(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync(); (long settingId, long settingValue) = Assert.Single(settings); Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId); _inboundControlStream = controlStream; }
public override async Task <HttpRequestData> ReadRequestDataAsync(bool readBody = true) { Http3LoopbackStream stream = await AcceptRequestStreamAsync().ConfigureAwait(false); return(await stream.ReadRequestDataAsync(readBody).ConfigureAwait(false)); }