Exemplo n.º 1
0
        private async Task DoSend()
        {
            Exception shutdownReason  = null;
            Exception unexpectedError = null;

            try
            {
                await ProcessSends();
            }
            catch (SocketException ex) when(IsConnectionResetError(ex.SocketErrorCode))
            {
                shutdownReason = new ConnectionResetException(ex.Message, ex);
                _trace.ConnectionReset(ConnectionId);
            }
            catch (Exception ex)
                when((ex is SocketException socketEx && IsConnectionAbortError(socketEx.SocketErrorCode)) ||
                     ex is ObjectDisposedException)
                {
                    // This should always be ignored since Shutdown() must have already been called by Abort().
                    shutdownReason = ex;
                }
            catch (Exception ex)
            {
                shutdownReason  = ex;
                unexpectedError = ex;
                _trace.ConnectionError(ConnectionId, unexpectedError);
            }
            finally
            {
                Shutdown(shutdownReason);

                // Complete the output after disposing the socket
                Output.Complete(unexpectedError);

                // Cancel any pending flushes so that the input loop is un-paused
                Input.CancelPendingFlush();
            }
        }
Exemplo n.º 2
0
        private async Task DoReceive()
        {
            Exception error = null;

            try { await ProcessReceives().ConfigureAwait(false); }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.ConnectionReset)
            {
                error = new ConnectionResetException(ex.Message, ex);
            }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.OperationAborted ||
                                            ex.SocketErrorCode == SocketError.ConnectionAborted ||
                                            ex.SocketErrorCode == SocketError.Interrupted ||
                                            ex.SocketErrorCode == SocketError.InvalidArgument)
            {
                if (!_aborted)
                {
                    // Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
                    error = new ConnectionAbortedException();
                }
            }
            catch (ObjectDisposedException) {
                if (!_aborted)
                {
                    error = new ConnectionAbortedException();
                }
            }
            catch (IOException ex) { error = ex; }
            catch (Exception ex) { error = new IOException(ex.Message, ex); }
            finally
            {
                if (_aborted)
                {
                    error ??= new ConnectionAbortedException();
                }

                await _application.Output.CompleteAsync(error).ConfigureAwait(false);
            }
        }
Exemplo n.º 3
0
        // Returns whether the read handles are still needed
        private bool HandleCompleteReadError(Ring ring, int result)
        {
            if (result == 0)
            {
                // EOF
                CompleteInbound(ring, null);
                return(false);
            }

            var err = -result;

            if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR)
            {
                Read(ring);
                return(true);
            }

            if (HasFlag(ConnectionState.ReadCancelled) && err == ECANCELED)
            {
                return(false);
            }

            Exception ex;

            if (err == ECONNRESET)
            {
                ex = new ErrnoException(ECONNRESET);
                ex = new ConnectionResetException(ex.Message, ex);
            }
            else
            {
                ex = new ErrnoException(err);
            }

            CompleteInbound(ring, ex);
            return(false);
        }
Exemplo n.º 4
0
        private void OnRead(UvStreamHandle handle, int status)
        {
            if (status == 0)
            {
                // EAGAIN/EWOULDBLOCK so just return the buffer.
                // http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb
                Debug.Assert(_currentWritableBuffer != null);
                _currentWritableBuffer.Value.Commit();
            }
            else if (status > 0)
            {
                Log.ConnectionRead(ConnectionId, status);

                Debug.Assert(_currentWritableBuffer != null);
                var currentWritableBuffer = _currentWritableBuffer.Value;
                currentWritableBuffer.Advance(status);
                var flushTask = currentWritableBuffer.FlushAsync();

                if (!flushTask.IsCompleted)
                {
                    // We wrote too many bytes to the reader, so pause reading and resume when
                    // we hit the low water mark.
                    _ = ApplyBackpressureAsync(flushTask);
                }
            }
            else
            {
                // Given a negative status, it's possible that OnAlloc wasn't called.
                _currentWritableBuffer?.Commit();
                _socket.ReadStop();

                IOException error = null;

                if (status == LibuvConstants.EOF)
                {
                    Log.ConnectionReadFin(ConnectionId);
                }
                else
                {
                    handle.Libuv.Check(status, out var uvError);

                    // Log connection resets at a lower (Debug) level.
                    if (LibuvConstants.IsConnectionReset(status))
                    {
                        Log.ConnectionReset(ConnectionId);
                        error = new ConnectionResetException(uvError.Message, uvError);
                    }
                    else
                    {
                        Log.ConnectionError(ConnectionId, uvError);
                        error = new IOException(uvError.Message, uvError);
                    }
                }

                // Complete after aborting the connection
                Input.Complete(error);
            }

            // Cleanup state from last OnAlloc. This is safe even if OnAlloc wasn't called.
            _currentWritableBuffer = null;
            _bufferHandle.Dispose();
        }
Exemplo n.º 5
0
        private async Task StartCore()
        {
            try
            {
                OutputConsumer = new LibuvOutputConsumer(Output, Thread, _socket, ConnectionId, Log);

                StartReading();

                Exception inputError  = null;
                Exception outputError = null;

                try
                {
                    // This *must* happen after socket.ReadStart
                    // The socket output consumer is the only thing that can close the connection. If the
                    // output pipe is already closed by the time we start then it's fine since, it'll close gracefully afterwards.
                    await OutputConsumer.WriteOutputAsync();
                }
                catch (UvException ex)
                {
                    // The connection reset/error has already been logged by LibuvOutputConsumer
                    if (ex.StatusCode == LibuvConstants.ECANCELED)
                    {
                        // Connection was aborted.
                    }
                    else if (LibuvConstants.IsConnectionReset(ex.StatusCode))
                    {
                        // Don't cause writes to throw for connection resets.
                        inputError = new ConnectionResetException(ex.Message, ex);
                    }
                    else
                    {
                        // This is unexpected.
                        Log.ConnectionError(ConnectionId, ex);

                        inputError  = ex;
                        outputError = ex;
                    }
                }
                finally
                {
                    inputError ??= _abortReason ?? new ConnectionAbortedException("The libuv transport's send loop completed gracefully.");

                    // Now, complete the input so that no more reads can happen
                    Input.Complete(inputError);
                    Output.Complete(outputError);

                    // Make sure it isn't possible for a paused read to resume reading after calling uv_close
                    // on the stream handle
                    Input.CancelPendingFlush();

                    // Send a FIN
                    Log.ConnectionWriteFin(ConnectionId, inputError.Message);

                    // We're done with the socket now
                    _socket.Dispose();

                    // Ensure this always fires
                    FireConnectionClosed();

                    await _waitForConnectionClosedTcs.Task;
                }
            }
            catch (Exception e)
            {
                Log.LogCritical(0, e, $"{nameof(LibuvConnection)}.{nameof(Start)}() {ConnectionId}");
            }
        }
Exemplo n.º 6
0
        private async Task DoReceive()
        {
            Exception error = null;

            try
            {
                while (true)
                {
                    // Ensure we have some reasonable amount of buffer space
                    var buffer = Input.GetMemory(MinAllocBufferSize);

                    var bytesReceived = await _receiver.ReceiveAsync(buffer);

                    if (bytesReceived == 0)
                    {
                        // FIN
                        _trace.ConnectionReadFin(ConnectionId);
                        break;
                    }

                    Input.Advance(bytesReceived);


                    var flushTask = Input.FlushAsync();

                    if (!flushTask.IsCompleted)
                    {
                        _trace.ConnectionPause(ConnectionId);

                        await flushTask;

                        _trace.ConnectionResume(ConnectionId);
                    }

                    var result = flushTask.GetAwaiter().GetResult();
                    if (result.IsCompleted)
                    {
                        // Pipe consumer is shut down, do we stop writing
                        break;
                    }
                }
            }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.ConnectionReset)
            {
                error = new ConnectionResetException(ex.Message, ex);
                _trace.ConnectionReset(ConnectionId);
            }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.OperationAborted ||
                                            ex.SocketErrorCode == SocketError.ConnectionAborted ||
                                            ex.SocketErrorCode == SocketError.Interrupted ||
                                            ex.SocketErrorCode == SocketError.InvalidArgument)
            {
                if (!_aborted)
                {
                    // Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
                    error = new ConnectionAbortedException();
                    _trace.ConnectionError(ConnectionId, error);
                }
            }
            catch (ObjectDisposedException)
            {
                if (!_aborted)
                {
                    error = new ConnectionAbortedException();
                    _trace.ConnectionError(ConnectionId, error);
                }
            }
            catch (IOException ex)
            {
                error = ex;
                _trace.ConnectionError(ConnectionId, error);
            }
            catch (Exception ex)
            {
                error = new IOException(ex.Message, ex);
                _trace.ConnectionError(ConnectionId, error);
            }
            finally
            {
                if (_aborted)
                {
                    error = error ?? new ConnectionAbortedException();
                }

                Input.Complete(error);
            }
        }
Exemplo n.º 7
0
        private async Task DoSend()
        {
            Exception?shutdownReason  = null;
            Exception?unexpectedError = null;

            try
            {
                while (true)
                {
                    var result = await Output.ReadAsync();

                    if (result.IsCanceled)
                    {
                        break;
                    }
                    var buffer = result.Buffer;

                    if (!buffer.IsEmpty)
                    {
                        _sender = _socketSenderPool.Rent();
                        await _sender.SendAsync(_socket, buffer);

                        // We don't return to the pool if there was an exception, and
                        // we keep the _sender assigned so that we can dispose it in StartAsync.
                        _socketSenderPool.Return(_sender);
                        _sender = null;
                    }

                    Output.AdvanceTo(buffer.End);

                    if (result.IsCompleted)
                    {
                        break;
                    }
                }
            }
            catch (SocketException ex) when(IsConnectionResetError(ex.SocketErrorCode))
            {
                shutdownReason = new ConnectionResetException(ex.Message, ex);
                _trace.ConnectionReset(this);
            }
            catch (Exception ex)
                when((ex is SocketException socketEx && IsConnectionAbortError(socketEx.SocketErrorCode)) ||
                     ex is ObjectDisposedException)
                {
                    // This should always be ignored since Shutdown() must have already been called by Abort().
                    shutdownReason = ex;
                }
            catch (Exception ex)
            {
                shutdownReason  = ex;
                unexpectedError = ex;
                _trace.ConnectionError(this, unexpectedError);
            }
            finally
            {
                Shutdown(shutdownReason);

                // Complete the output after disposing the socket
                Output.Complete(unexpectedError);

                // Cancel any pending flushes so that the input loop is un-paused
                Input.CancelPendingFlush();
            }
        }
Exemplo n.º 8
0
        private async Task DoReceive()
        {
            Exception?error = null;

            try
            {
                while (true)
                {
                    if (_waitForData)
                    {
                        // Wait for data before allocating a buffer.
                        await _receiver.WaitForDataAsync(_socket);
                    }

                    // Ensure we have some reasonable amount of buffer space
                    var buffer = Input.GetMemory(MinAllocBufferSize);

                    var bytesReceived = await _receiver.ReceiveAsync(_socket, buffer);

                    if (bytesReceived == 0)
                    {
                        // FIN
                        _trace.ConnectionReadFin(this);
                        break;
                    }

                    Input.Advance(bytesReceived);

                    var flushTask = Input.FlushAsync();

                    var paused = !flushTask.IsCompleted;

                    if (paused)
                    {
                        _trace.ConnectionPause(this);
                    }

                    var result = await flushTask;

                    if (paused)
                    {
                        _trace.ConnectionResume(this);
                    }

                    if (result.IsCompleted || result.IsCanceled)
                    {
                        // Pipe consumer is shut down, do we stop writing
                        break;
                    }
                }
            }
            catch (SocketException ex) when(IsConnectionResetError(ex.SocketErrorCode))
            {
                // This could be ignored if _shutdownReason is already set.
                error = new ConnectionResetException(ex.Message, ex);

                // There's still a small chance that both DoReceive() and DoSend() can log the same connection reset.
                // Both logs will have the same ConnectionId. I don't think it's worthwhile to lock just to avoid this.
                if (!_socketDisposed)
                {
                    _trace.ConnectionReset(this);
                }
            }
            catch (Exception ex)
                when((ex is SocketException socketEx && IsConnectionAbortError(socketEx.SocketErrorCode)) ||
                     ex is ObjectDisposedException)
                {
                    // This exception should always be ignored because _shutdownReason should be set.
                    error = ex;

                    if (!_socketDisposed)
                    {
                        // This is unexpected if the socket hasn't been disposed yet.
                        _trace.ConnectionError(this, error);
                    }
                }
            catch (Exception ex)
            {
                // This is unexpected.
                error = ex;
                _trace.ConnectionError(this, error);
            }
            finally
            {
                // If Shutdown() has already bee called, assume that was the reason ProcessReceives() exited.
                Input.Complete(_shutdownReason ?? error);

                FireConnectionClosed();

                await _waitForConnectionClosedTcs.Task;
            }
        }
        private async Task DoSendAsync()
        {
            Exception shutdownReason  = null;
            Exception unexpectedError = null;

            try
            {
                RegisteredOperationContext operationContext = _socket.CreateOperationContext();
                var sendBuffers = new Operation[1];

                while (true)
                {
                    ReadResult readResult = await _socketInput.ReadAsync();

                    if (readResult.IsCanceled)
                    {
                        break;
                    }

                    ReadOnlySequence <byte> buffer = readResult.Buffer;
                    SequencePosition        next = buffer.Start, end = buffer.End;
                    int sendCount = 0;

                    while (buffer.TryGet(ref next, out ReadOnlyMemory <byte> segment))
                    {
                        if (sendCount == sendBuffers.Length)
                        {
                            Array.Resize(ref sendBuffers, sendBuffers.Length * 2);
                        }

                        RegisteredOperationContext ctx = sendBuffers[sendCount].Context ??= _socket.CreateOperationContext();
                        sendBuffers[sendCount++].Task = ctx.SendAsync(segment);
                    }

                    for (int i = 0; i < sendCount; ++i)
                    {
                        await sendBuffers[i].Task;
                    }

                    _socketInput.AdvanceTo(end);

                    if (readResult.IsCompleted)
                    {
                        break;
                    }
                }
            }
            catch (SocketException ex) when(IsConnectionResetError(ex.SocketErrorCode))
            {
                shutdownReason = new ConnectionResetException(ex.Message, ex);
            }
            catch (SocketException ex) when(IsConnectionAbortError(ex.SocketErrorCode))
            {
                shutdownReason = ex;
            }
            catch (ObjectDisposedException ex)
            {
                shutdownReason = ex;
            }
            catch (Exception ex)
            {
                shutdownReason  = ex;
                unexpectedError = ex;
            }

            Shutdown(shutdownReason);
            _socketInput.Complete(unexpectedError);
            _socketOutput.CancelPendingFlush();
        }
Exemplo n.º 10
0
        private async Task DoReceive()
        {
            Exception error = null;

            try
            {
                while (true)
                {
                    // Ensure we have some reasonable amount of buffer space
                    var buffer = _input.Alloc(MinAllocBufferSize);

                    try
                    {
                        var bytesReceived = await _socket.ReceiveAsync(GetArraySegment(buffer.Buffer), SocketFlags.None);

                        if (bytesReceived == 0)
                        {
                            // FIN
                            break;
                        }

                        buffer.Advance(bytesReceived);
                    }
                    finally
                    {
                        buffer.Commit();
                    }

                    var result = await buffer.FlushAsync();

                    if (result.IsCompleted)
                    {
                        // Pipe consumer is shut down, do we stop writing
                        break;
                    }
                }
            }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.ConnectionReset)
            {
                error = new ConnectionResetException(ex.Message, ex);
            }
            catch (SocketException ex) when(ex.SocketErrorCode == SocketError.OperationAborted)
            {
                error = new ConnectionAbortedException();
            }
            catch (ObjectDisposedException)
            {
                error = new ConnectionAbortedException();
            }
            catch (IOException ex)
            {
                error = ex;
            }
            catch (Exception ex)
            {
                error = new IOException(ex.Message, ex);
            }
            finally
            {
                _connectionContext.Abort(error);
                _input.Complete(error);
            }
        }