Exemple #1
0
        internal async Task <NotHttpConnection> GetRequestConnection(NHClientRequest caller)
        {
            NHClientEndpoint         endpoint;
            Task <NotHttpConnection> blockTask;

            lock (this)
            {
                Uri url = caller.Url;

                Dictionary <int, NHClientEndpoint> hostPorts;
                if (!connections.TryGetValue(url.Host, out hostPorts))
                {
                    hostPorts = new Dictionary <int, NHClientEndpoint>();
                    connections.Add(url.Host, hostPorts);
                }

                if (!hostPorts.TryGetValue(url.Port, out endpoint))
                {
                    endpoint = new NHClientEndpoint(url.Host, url.Port, isOverlapped, maxConnections, idleTimeout, logger);
                    hostPorts.Add(url.Port, endpoint);
                }

                blockTask = endpoint.WaitForRequestSlot(caller);
            }

            return(await blockTask);
        }
Exemple #2
0
        public Exception AssignRequest(NHClientRequest request)
        {
            lock (this)
            {
                if (currentRequest != null)
                {
                    endpoint.Logger.Log("Error: callers out of sync");
                    throw new ApplicationException("Callers out of sync");
                }

                // if exception is non-null the connection is faulted, and
                // will be discarded by the caller when this method returns
                if (exception == null)
                {
                    currentRequest = request;
                    if (outstandingRequests == 0)
                    {
                        idleTask.SetResult(false);
                    }
                    ++outstandingRequests;
                }

                return(exception);
            }
        }
Exemple #3
0
        public void StreamFaulted(Exception e)
        {
            lock (this)
            {
                if (stream == null)
                {
                    // already faulted
                    return;
                }

                StreamFaultedInternal(e);

                if (!endpoint.IsOverlapped)
                {
                    currentRequest = null;
                }
            }

            if (!endpoint.IsOverlapped)
            {
                // take ourselves out of the endpoint's list of available connections; ReportConnectionReady
                // checks to see if we are faulted. For an overlapped connection this will have happened in
                // ReportRequestCompleted already
                endpoint.ReportConnectionReady(this);
            }
        }
Exemple #4
0
        public int NextSequenceNumber(NHClientRequest caller)
        {
            lock (this)
            {
                if (caller != currentRequest)
                {
                    endpoint.Logger.Log("Error: callers out of sync");
                    throw new ApplicationException("Callers out of sync");
                }

                // I don't believe in foo++ syntax obfuscation :)
                int sn = sequenceNumber;
                ++sequenceNumber;
                return(sn);
            }
        }
Exemple #5
0
        public void ReportRequestCompleted(NHClientRequest caller)
        {
            lock (this)
            {
                if (caller != currentRequest)
                {
                    endpoint.Logger.Log("Error: callers out of sync");
                    throw new ApplicationException("Callers out of sync");
                }

                if (endpoint.IsOverlapped)
                {
                    currentRequest = null;
                }
            }

            if (endpoint.IsOverlapped)
            {
                endpoint.ReportConnectionReady(this);
            }
        }
Exemple #6
0
        public async Task <NotHttpConnection> WaitForRequestSlot(NHClientRequest caller)
        {
            Task <NotHttpConnection> blockTask = null;

            lock (this)
            {
                // this is where we garbage collection connections that are faulted, so
                // go through the idle connections throwing them away if they
                // aren't good until we match one
                while (blockTask == null && idleConnections.Count > 0)
                {
                    NotHttpConnection connection = idleConnections.Pop();
                    Exception         exception  = connection.AssignRequest(caller);
                    if (exception == null)
                    {
                        blockTask = Task.FromResult(connection);
                    }
                    else
                    {
                        --openedConnections;
                        logger.Log("Removing faulted connection, " + openedConnections + " remaining: " + exception.Message);
                    }
                }

                if (blockTask == null)
                {
                    RequestWaiter waiter = new RequestWaiter(caller);
                    requestWaiters.AddLast(waiter);
                    blockTask = waiter.task.Task;
                }

                ReOpenConnections();
            }

            return(await blockTask);
        }
Exemple #7
0
 public RequestWaiter(NHClientRequest caller)
 {
     request = caller;
     task    = new TaskCompletionSource <NotHttpConnection>();
 }
Exemple #8
0
        private async Task ReadStream()
        {
            int responseSequenceNumber = -1;

            while (true)
            {
                Task <bool> idleBlocker = null;
                lock (this)
                {
                    if (outstandingRequests == 0)
                    {
                        // add ContinueWith so the continuation is asynchronous, since await generates
                        // a synchronous continuation
                        idleBlocker = idleTask.Task.ContinueWith((t) => t.Result);
                    }
                }

                if (idleBlocker != null)
                {
                    bool mustExit = await idleBlocker;
                    if (mustExit)
                    {
                        endpoint.Logger.Log("Reader exiting due to idle connection");
                        return;
                    }
                }

                if (!endpoint.IsOverlapped)
                {
                    // sequence number is 0 for the first response
                    ++responseSequenceNumber;
                }

                // for an overlapped connection we pass in responseSequenceNumber=-1 and the actual sequence
                // number is read from the protocol. For a sequential connection we pass in the actual sequence
                // number
                NHClientResponse response = new NHClientResponse(this, responseSequenceNumber, endpoint.Logger);
                try
                {
                    NotHttp.NHError error      = new NotHttp.NHError();
                    bool            gotRequest = await response.Parse(null, stream, error);

                    if (!gotRequest)
                    {
                        throw new ApplicationException("Server closed connection");
                    }

                    if (response.StatusCode != HttpStatusCode.OK || endpoint.IsOverlapped)
                    {
                        // fetch the whole response now, so we're ready to wait for the next message even if
                        // the caller isn't ready to read this one
                        await response.BufferInput();
                    }

                    lock (this)
                    {
                        TaskCompletionSource <IHttpResponse> tcs;
                        if (pendingClients.TryGetValue(response.SequenceNumber, out tcs))
                        {
                            // run this asynchronously outside the lock
                            Task abandon = Task.Run(() => ReturnResult(tcs, response));
                            pendingClients.Remove(response.SequenceNumber);
                        }
                        else
                        {
                            if (receivedResponses.ContainsKey(response.SequenceNumber))
                            {
                                StreamFaulted(new ApplicationException("Received duplicate sequence numbers " + response.SequenceNumber));
                                return;
                            }

                            receivedResponses.Add(response.SequenceNumber, response);
                        }
                    }

                    // wait until the caller has consumed all the payload data (if we buffered it above this
                    // falls through immediately)
                    await response.Completed;

                    string closeHeader = response.HeadersInternal[HttpRequestHeader.Connection];
                    if (closeHeader != null && closeHeader.ToLower() == "close")
                    {
                        endpoint.Logger.Log("Passing stream connection closed header");
                        StreamFaulted(new ApplicationException("Connection closed by server request"));
                        return;
                    }

                    lock (this)
                    {
                        if (!endpoint.IsOverlapped)
                        {
                            currentRequest = null;
                        }

                        --outstandingRequests;
                        if (outstandingRequests == 0)
                        {
                            idleTask = new TaskCompletionSource <bool>();
                            // capture member variable in local scope to give to closure
                            TaskCompletionSource <bool> currentIdleTask = idleTask;
                            Task abandon = Task.Delay(endpoint.IdleTimeout).ContinueWith((t) => ExitOnIdle(currentIdleTask));
                        }
                    }

                    if (!endpoint.IsOverlapped)
                    {
                        endpoint.ReportConnectionReady(this);
                    }
                }
                catch (Exception e)
                {
                    endpoint.Logger.Log("Passing exception status " + response.StatusCode);
                    StreamFaulted(e);
                    return;
                }
            }
        }