public static async Task RunSessionAsync(AppFunc app, Func<Http2ClientSession, Http2ServerSession, Task> sessionOperations) { DuplexStream clientStream = new DuplexStream(); DuplexStream serverStream = clientStream.GetOpositeStream(); Task clientTask, serverTask; using (Http2ClientSession clientSession = new Http2ClientSession(clientStream, false, CancellationToken.None, CancellationToken.None)) { using (Http2ServerSession serverSession = new Http2ServerSession(app, new TransportInformation())) { clientTask = clientSession.Start(); serverTask = serverSession.Start(serverStream, CancellationToken.None); await sessionOperations(clientSession, serverSession); } } await clientTask; await serverTask; }
public void ConnectionReset_StreamsAborted() { DuplexStream clientStream = new DuplexStream(); DuplexStream serverStream = clientStream.GetOpositeStream(); ManualResetEvent waitForRequest = new ManualResetEvent(false); ManualResetEvent waitForCancel = new ManualResetEvent(false); AppFunc app = environment => { OwinResponse response = new OwinResponse(environment); TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); response.CallCancelled.Register(() => { waitForCancel.Set(); tcs.TrySetCanceled(); }); waitForRequest.Set(); return tcs.Task; }; Task clientTask, serverTask; using (Http2ClientSession clientSession = new Http2ClientSession(clientStream, false, CancellationToken.None, CancellationToken.None)) { using (Http2ServerSession serverSession = new Http2ServerSession(app, CreateTransportInfo())) { clientTask = clientSession.Start(); serverTask = serverSession.Start(serverStream, CancellationToken.None); IList<KeyValuePair<string, string>> requestPairs = GenerateHeaders("GET"); Http2ClientStream clientProtocolStream = clientSession.SendRequest(requestPairs, null, 3, false, CancellationToken.None); Task<IList<KeyValuePair<string, string>>> responseTask = clientProtocolStream.GetResponseAsync(); waitForRequest.WaitOne(); clientStream.Abort(); Assert.Throws<AggregateException>(() => responseTask.Result); Assert.Throws<AggregateException>(() => clientTask.Wait(1000)); Assert.True(serverTask.Wait(1000)); Assert.True(waitForCancel.WaitOne(1000)); } } }
// Returns true if this request was used to perform the initial handshake and does not need to be submitted separately. private async Task<bool> ConnectIfNeeded(HttpRequestMessage request, CancellationToken cancel) { Stream sessionStream = null; // Async lock, only one at a time. await _connectingLock.WaitAsync(); try { if (_clientSession == null) { if (_do11Handshake && !IsHanshakeCompatableRequest(request)) { return false; } sessionStream = await ConnectAsync(request, cancel); bool didHandshake = _do11Handshake; if (_do11Handshake) { HandshakeResponse handshake = await DoHandshakeAsync(sessionStream, request, cancel); if (handshake.Result == HandshakeResult.NonUpgrade) { _fallbackTo11 = true; // TODO: Rather than resetting the connection, we could just download the response. sessionStream.Dispose(); return false; /* throw new NotSupportedException("HTTP/1.1 handshake fallback not implemented: \r\n" + FrameHelpers.GetAsciiAt(handshake.ResponseBytes)); */ } else if (handshake.Result == HandshakeResult.UnexpectedControlFrame || handshake.Result == HandshakeResult.UnexpectedConnectionClose) { // The server only accepts direct 2.0 upgrade, try again without the 1.1 handshake. didHandshake = false; sessionStream.Dispose(); sessionStream = await ConnectAsync(request, cancel); } else if (handshake.Result != HandshakeResult.Upgrade) { throw new NotImplementedException(handshake.Result.ToString()); } } _clientSession = new Http2ClientSession(sessionStream, createHandshakeStream: didHandshake, handshakeCancel: didHandshake ? cancel : CancellationToken.None, cancel: CancellationToken.None); // TODO: Listen to task for errors? Dispose/Abort CT? Task pumpTasks = _clientSession.Start(); _connectingLock.Release(999); // Unblock all, this method no longer needs to be one at a time. return didHandshake; } return false; } catch (Exception) { if (sessionStream != null) { sessionStream.Dispose(); } if (_clientSession != null) { _clientSession.Dispose(); _clientSession = null; } throw; } finally { _connectingLock.Release(); } }