void InvokeRequestHandler(RequestCommandAndStream requestAndStream)
        {
            ListenerCommand.RequestCommand requestCommand = requestAndStream.RequestCommand;
            Uri requestUri      = new Uri(this.listener.Address, requestCommand.RequestTarget);
            var listenerContext = new RelayedHttpListenerContext(
                this.listener,
                requestUri,
                requestCommand.Id,
                requestCommand.Method,
                requestCommand.RequestHeaders);

            listenerContext.Request.SetRemoteAddress(requestCommand.RemoteEndpoint);
            listenerContext.Response.StatusCode   = HttpStatusCode.OK;
            listenerContext.Response.OutputStream = new ResponseStream(this, listenerContext);

            RelayEventSource.Log.HybridHttpRequestReceived(listenerContext.TrackingContext, requestCommand.Method);

            Stream requestStream = requestAndStream.Stream;

            if (requestStream != null)
            {
                listenerContext.Request.HasEntityBody = true;
                listenerContext.Request.InputStream   = requestStream;
            }

            var requestHandler = this.listener.RequestHandler;

            if (requestHandler != null)
            {
                try
                {
                    RelayEventSource.Log.HybridHttpInvokingUserRequestHandler();
                    requestHandler(listenerContext);
                }
                catch (Exception userException) when(!Fx.IsFatal(userException))
                {
                    RelayEventSource.Log.HandledExceptionAsWarning(this, userException);
                    listenerContext.Response.StatusCode        = HttpStatusCode.InternalServerError;
                    listenerContext.Response.StatusDescription = this.TrackingContext.EnsureTrackableMessage(SR.RequestHandlerException);
                    listenerContext.Response.CloseAsync().Fork(this);
                    return;
                }
            }
            else
            {
                RelayEventSource.Log.HybridHttpConnectionMissingRequestHandler();
                listenerContext.Response.StatusCode        = HttpStatusCode.NotImplemented;
                listenerContext.Response.StatusDescription = this.TrackingContext.EnsureTrackableMessage(SR.RequestHandlerMissing);
                listenerContext.Response.CloseAsync().Fork(this);
            }
        }
        void InvokeRequestHandler(RequestCommandAndStream requestAndStream)
        {
            ListenerCommand.RequestCommand requestCommand = requestAndStream.RequestCommand;
            Uri requestUri      = new Uri(this.listener.Address, requestCommand.RequestTarget);
            var listenerContext = new RelayedHttpListenerContext(
                this.listener,
                requestUri,
                requestCommand.Id,
                requestCommand.Method,
                requestCommand.RequestHeaders);

            listenerContext.Request.SetRemoteAddress(requestCommand.RemoteEndpoint);
            listenerContext.Response.StatusCode   = HttpStatusCode.OK;
            listenerContext.Response.OutputStream = new ResponseStream(this, listenerContext);

            RelayEventSource.Log.HybridHttpRequestReceived(listenerContext.TrackingContext, requestCommand.Method);

            Stream requestStream = requestAndStream.Stream;

            if (requestStream != null)
            {
                listenerContext.Request.HasEntityBody = true;
                listenerContext.Request.InputStream   = requestStream;
            }

            var requestHandler = this.listener.RequestHandler;

            if (requestHandler != null)
            {
                try
                {
                    RelayEventSource.Log.HybridHttpInvokingUserRequestHandler();
                    requestHandler(listenerContext);
                }
                catch (Exception userException) when(!Fx.IsFatal(userException))
                {
                    RelayEventSource.Log.Warning(
                        listenerContext,
                        $"The Relayed Listener's custom RequestHandler threw an exception. {listenerContext.TrackingContext}, Exception: {userException}");
                    return;
                }
            }
            else
            {
                RelayEventSource.Log.HandledExceptionAsWarning(this, new InvalidOperationException("RequestHandler is not set."));
            }
        }
        public static async Task CreateAsync(HybridConnectionListener listener, ListenerCommand.RequestCommand requestCommand, WebSocket controlWebSocket)
        {
            var hybridHttpConnection = new HybridHttpConnection(listener, controlWebSocket, requestCommand.Address);

            // In this method we're holding up the listener's control connection.
            // Do only what we need to do (receive any request body from control channel) and then let this Task complete.
            bool requestOverControlConnection = requestCommand.Body.HasValue;
            var  requestAndStream             = new RequestCommandAndStream(requestCommand, null);

            if (requestOverControlConnection)
            {
                requestAndStream = await hybridHttpConnection.ReceiveRequestBodyOverControlAsync(requestCommand).ConfigureAwait(false);
            }

            // ProcessFirstRequestAsync runs without blocking the listener control connection:
            Task.Run(() => hybridHttpConnection.ProcessFirstRequestAsync(requestAndStream)).Fork(hybridHttpConnection);
        }
        async Task ProcessFirstRequestAsync(RequestCommandAndStream requestAndStream)
        {
            try
            {
                var requestCommand = requestAndStream.RequestCommand;
                if (!requestCommand.Body.HasValue)
                {
                    // Need to rendezvous to get the real RequestCommand
                    requestAndStream = await this.ReceiveRequestOverRendezvousAsync().ConfigureAwait(false);
                }

                this.InvokeRequestHandler(requestAndStream);
            }
            catch (Exception e) when(!Fx.IsFatal(e))
            {
                RelayEventSource.Log.HandledExceptionAsWarning(this.listener, e);
                await this.CloseAsync().ConfigureAwait(false);
            }
        }