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();
            }
        }