示例#1
0
        public static ListenerTask Create(ListenerContext context, ConnectionState state,
                                          Func <Task> start, Func <ConnectionState> continuation)
        {
            Func <Task <object> > func = async() => { await start().ConfigureAwait(false); return(null); };

            return(new ListenerTask <object> (context, state, func, r => continuation()));
        }
示例#2
0
 internal void AssignContext(ListenerContext context)
 {
     hasInstrumentation = true;
     AssignedContext    = context;
     context.AssignContext(this);
     contextTask.TrySetResult(context);
 }
示例#3
0
 protected override void Close()
 {
     if (targetContext != null)
     {
         targetContext.Dispose();
         targetContext = null;
     }
     base.Close();
 }
示例#4
0
        internal async Task <HttpResponse> HandleRequest(
            TestContext ctx, ListenerContext context, HttpConnection connection,
            HttpRequest request, CancellationToken cancellationToken)
        {
            var me = $"{ME} HANDLE REQUEST";

            ctx.LogDebug(2, $"{me} {connection.ME} {request}");

            OnInit();

            HttpResponse response;

            try {
                cancellationToken.ThrowIfCancellationRequested();
                if (!Operation.HasAnyFlags(HttpOperationFlags.DontReadRequestBody))
                {
                    await request.Read(ctx, cancellationToken).ConfigureAwait(false);

                    ctx.LogDebug(2, $"{me} REQUEST FULLY READ");
                }
                else
                {
                    await request.ReadHeaders(ctx, cancellationToken).ConfigureAwait(false);

                    ctx.LogDebug(2, $"{me} REQUEST HEADERS READ");
                }

                response = await Handler.HandleRequest(
                    ctx, Operation, connection, request, cancellationToken).ConfigureAwait(false);

                ctx.LogDebug(2, $"{me} HANDLE REQUEST DONE: {response}");
            } catch (OperationCanceledException) {
                OnCanceled();
                throw;
            } catch (Exception ex) {
                OnError(ex);
                throw;
            }

            if (response.Redirect == null)
            {
                OnFinished();
                return(response);
            }

            response.Redirect.parentOperation = this;

            return(response);
        }
示例#5
0
 internal ListenerOperation Redirect(ListenerContext newContext)
 {
     if (State == ConnectionState.NeedContextForRedirect)
     {
         redirectContext = newContext;
         State           = ConnectionState.RequestComplete;
         return(currentResponse.Redirect);
     }
     if (State == ConnectionState.CannotReuseConnection)
     {
         State = ConnectionState.Closed;
         return(assignedOperation);
     }
     throw new InvalidOperationException();
 }
示例#6
0
        public ListenerContext ReuseConnection()
        {
            disposed = true;
            var oldConnection = Interlocked.Exchange(ref connection, null);

            if (oldConnection == null)
            {
                throw new InvalidOperationException();
            }

            var newContext = new ListenerContext(Listener, oldConnection, true)
            {
                State = Listener.UsingInstrumentation ? ConnectionState.WaitingForContext : ConnectionState.WaitingForRequest
            };

            assignedOperation = null;
            currentOperation  = null;
            State             = ConnectionState.Closed;
            return(newContext);
        }
示例#7
0
        internal ListenerOperation GetOperation(ListenerContext context, HttpRequest request)
        {
            ListenerOperation operation;

            lock (this) {
                var me = $"{nameof (GetOperation)}({context.Connection.ME})";
                Debug($"{me} {request.Method} {request.Path} {request.Protocol}");

                if (!registry.ContainsKey(request.Path))
                {
                    Debug($"{me} INVALID PATH: {request.Path}!");
                    return(null);
                }

                operation = registry[request.Path];
                registry.Remove(request.Path);
                Server.BumpRequestCount();
            }
            ParentListener?.UnregisterOperation(operation);
            return(operation);
        }
示例#8
0
        public async Task <Response> RunWithContext(TestContext ctx, ListenerOperation operation, Request request,
                                                    ClientFunc clientFunc, CancellationToken cancellationToken)
        {
            var me = $"{ME}({operation.Operation.ME}) RUN WITH CONTEXT";

            ListenerContext context        = null;
            ListenerContext targetContext  = null;
            var             reusing        = !operation.Operation.HasAnyFlags(HttpOperationFlags.DontReuseConnection);
            var             delayedContext = operation.Operation.HasAnyFlags(HttpOperationFlags.DelayedListenerContext);

            /*
             * When using HttpOperationFlags.DelayedListenerContext, then we call the `clientFunc` before we
             * actually start listening.
             */

            if (UsingInstrumentation && !delayedContext)
            {
                context = await FindContext(ctx, operation, reusing);

                reusing = context.ReusingConnection;

                ctx.LogDebug(2, $"{me} - CREATE CONTEXT: {reusing} {context.ME}");

                await context.ServerStartTask.ConfigureAwait(false);

                ctx.LogDebug(2, $"{me} - CREATE CONTEXT #1: {reusing} {context.ME}");
            }

            if (TargetListener?.UsingInstrumentation ?? false)
            {
                targetContext = await TargetListener.FindContext(ctx, operation.TargetOperation, false);

                ctx.LogDebug(2, $"{me} - CREATE TARGET CONTEXT: {reusing} {targetContext.ME}");
                try {
                    await targetContext.ServerStartTask.ConfigureAwait(false);
                } catch {
                    context?.Dispose();
                    throw;
                }
            }

            var clientTask         = clientFunc(ctx, request, cancellationToken);
            var serverInitTask     = operation.ServerInitTask;
            var serverFinishedTask = operation.ServerFinishedTask;

            ExceptionDispatchInfo throwMe = null;
            bool initDone = false, serverDone = false, clientDone = false;

            while (!initDone || !serverDone || !clientDone)
            {
                ctx.LogDebug(2, $"{me} LOOP: init={initDone} server={serverDone} client={clientDone}");

                if (clientDone)
                {
                    if (operation.Operation.HasAnyFlags(
                            HttpOperationFlags.AbortAfterClientExits, HttpOperationFlags.ServerAbortsHandshake,
                            HttpOperationFlags.ClientAbortsHandshake))
                    {
                        ctx.LogDebug(2, $"{me} - ABORTING");
                        break;
                    }
                    if (!initDone)
                    {
                        ctx.LogDebug(2, $"{me} - ERROR: {clientTask.Result}");
                        throwMe = ExceptionDispatchInfo.Capture(new ConnectionException(
                                                                    $"{me} client exited before server accepted connection."));
                        break;
                    }
                }

                var tasks = new List <Task> ();
                if (!initDone)
                {
                    tasks.Add(serverInitTask);
                }
                if (!serverDone)
                {
                    tasks.Add(serverFinishedTask);
                }
                if (!clientDone)
                {
                    tasks.Add(clientTask);
                }
                var finished = await Task.WhenAny(tasks).ConfigureAwait(false);

                string which;
                if (finished == serverInitTask)
                {
                    which    = "init";
                    initDone = true;
                }
                else if (finished == serverFinishedTask)
                {
                    which      = "server";
                    serverDone = true;
                }
                else if (finished == clientTask)
                {
                    which      = "client";
                    clientDone = true;
                }
                else
                {
                    throwMe = ExceptionDispatchInfo.Capture(new InvalidOperationException());
                    break;
                }

                ctx.LogDebug(2, $"{me} #4: {which} exited - {finished.Status}");
                if (finished.Status == TaskStatus.Faulted || finished.Status == TaskStatus.Canceled)
                {
                    if (operation.Operation.HasAnyFlags(HttpOperationFlags.ExpectServerException) &&
                        (finished == serverFinishedTask || finished == serverInitTask))
                    {
                        ctx.LogDebug(2, $"{me} EXPECTED EXCEPTION {finished.Exception.GetType ()}");
                    }
                    else if (finished.Status == TaskStatus.Canceled)
                    {
                        ctx.LogDebug(2, $"{me} CANCELED");
                        throwMe = ExceptionDispatchInfo.Capture(new OperationCanceledException());
                        break;
                    }
                    else
                    {
                        ctx.LogDebug(2, $"{me} FAILED: {finished.Exception.Message}");
                        throwMe = ExceptionDispatchInfo.Capture(finished.Exception);
                        break;
                    }
                }
            }

            if (throwMe != null)
            {
                ctx.LogDebug(2, $"{me} THROWING {throwMe.SourceException.Message}");
                lock (this) {
                    operation.OnError(throwMe.SourceException);
                    if (context != null)
                    {
                        context.Dispose();
                    }
                    if (targetContext != null)
                    {
                        targetContext.Dispose();
                    }
                    mainLoopEvent.Set();
                }
                throwMe.Throw();
            }

            return(clientTask.Result);
        }
示例#9
0
        void RunScheduler()
        {
            do
            {
                Cleanup();

                if (!UsingInstrumentation)
                {
                    CreateConnections();
                }
            } while (!StartTasks());

            void Cleanup()
            {
                var iter = connections.First;

                while (iter != null)
                {
                    var node    = iter;
                    var context = node.Value;
                    iter = iter.Next;

                    if (context.State == ConnectionState.Closed)
                    {
                        connections.Remove(node);
                        context.Dispose();
                    }
                    else if (context.State == ConnectionState.ReuseConnection)
                    {
                        connections.Remove(node);
                        var newContext = context.ReuseConnection();
                        connections.AddLast(newContext);
                    }
                }
            }

            void CreateConnections()
            {
                while (connections.Count < requestParallelConnections)
                {
                    Debug($"RUN SCHEDULER: {connections.Count}");
                    var connection = Backend.CreateConnection();
                    connections.AddLast(new ListenerContext(this, connection, false));
                    Debug($"RUN SCHEDULER #1: {connection.ME}");
                }
            }

            bool StartTasks()
            {
                var             listening        = false;
                ListenerContext listeningContext = null;
                ListenerContext redirectContext  = null;
                ListenerContext idleContext      = null;

                var me = $"RUN SCHEDULER - TASKS";

                Debug($"{me}");

                var idx  = 0;
                var iter = connections.First;

                while (iter != null)
                {
                    var node    = iter;
                    var context = node.Value;
                    iter = iter.Next;

                    Debug($"{me} ({++idx}/{connections.Count}): {context.State} {context.CurrentTask != null} {context.ME}");

                    if (!listening && context.State == ConnectionState.Listening)
                    {
                        listening = true;
                    }

                    if (false && UsingInstrumentation && listening && context.State == ConnectionState.Listening)
                    {
                        continue;
                    }

                    if (context.CurrentTask != null)
                    {
                        continue;
                    }

                    switch (context.State)
                    {
                    case ConnectionState.Idle:
                    case ConnectionState.WaitingForContext:
                        if (!UsingInstrumentation)
                        {
                            throw new InvalidOperationException();
                        }
                        if (idleContext == null && listeningContext == null)
                        {
                            idleContext = context;
                        }
                        continue;

                    case ConnectionState.NeedContextForRedirect:
                    case ConnectionState.CannotReuseConnection:
                        if (redirectContext == null)
                        {
                            redirectContext = context;
                        }
                        continue;

                    case ConnectionState.Listening:
                        if (idleContext == null && listeningContext == null)
                        {
                            listeningContext = context;
                        }
                        break;
                    }

                    var task = context.MainLoopListenerTask(TestContext, cts.Token);
                    listenerTasks.AddLast(task);

                    if (false && context.State == ConnectionState.Listening && !listening)
                    {
                        listeningContext = context;
                        listening        = true;
                    }
                }

                Debug($"{me} DONE: instrumentation={currentOperation?.ID} listening={listening} " +
                      $"redirect={redirectContext != null} idleContext={idleContext != null} " +
                      $"listeningContext={listeningContext != null} " +
                      $"assigned={currentOperation?.AssignedContext != null}");

                var availableContext = idleContext ?? listeningContext;

                if (currentOperation != null)
                {
                    if (currentOperation.AssignedContext != null)
                    {
                        return(true);
                    }

                    var operation   = currentOperation.Operation;
                    var forceNewCnc = operation.HasAnyFlags(HttpOperationFlags.ForceNewConnection);
                    if (listening && !forceNewCnc)
                    {
                        return(true);
                    }

                    if (availableContext == null || forceNewCnc)
                    {
                        var connection = Backend.CreateConnection();
                        availableContext = new ListenerContext(this, connection, false);
                        connections.AddLast(availableContext);
                    }
                    Debug($"{me} ASSIGN CONTEXT: {availableContext.ME} {currentOperation.ME}");
                    currentOperation.AssignContext(availableContext);
                    return(false);
                }

                if (redirectContext == null)
                {
                    return(true);
                }

                if (availableContext == null)
                {
                    var connection = Backend.CreateConnection();
                    availableContext = new ListenerContext(this, connection, false);
                    connections.AddLast(availableContext);
                }

                currentOperation = redirectContext.Redirect(availableContext);
                currentOperation.AssignContext(availableContext);
                Debug($"{me} DONE #1: {availableContext.ME} {currentOperation?.ME}");
                return(false);
            }
        }
示例#10
0
 public static ListenerTask Create <T, U> (ListenerContext context, ConnectionState state,
                                           Func <Task <(T, U)> > start, Func <T, U, ConnectionState> continuation)
示例#11
0
 public static ListenerTask Create <T> (ListenerContext context, ConnectionState state,
                                        Func <Task <T> > start, Func <T, ConnectionState> continuation)
 {
     return(new ListenerTask <T> (context, state, start, continuation));
 }