public async Task Start() { 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 { inputError = ex; outputError = ex; } } finally { // Now, complete the input so that no more reads can happen Input.Complete(inputError ?? _abortReason ?? new ConnectionAbortedException()); 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); // We're done with the socket now _socket.Dispose(); ThreadPool.QueueUserWorkItem(state => ((LibuvConnection)state).CancelConnectionClosedToken(), this); } } catch (Exception e) { Log.LogCritical(0, e, $"{nameof(LibuvConnection)}.{nameof(Start)}() {ConnectionId}"); } }
private Exception LogAndWrapReadError(UvException uvError) { if (uvError.StatusCode == LibuvConstants.ECANCELED) { // The operation was canceled by the server not the client. No need for additional logs. return(new ConnectionAbortedException(uvError.Message, uvError)); } else if (LibuvConstants.IsConnectionReset(uvError.StatusCode)) { // Log connection resets at a lower (Debug) level. Log.ConnectionReset(ConnectionId); return(new ConnectionResetException(uvError.Message, uvError)); } else { Log.ConnectionError(ConnectionId, uvError); return(new IOException(uvError.Message, uvError)); } }
/// <summary> /// Handles an incoming connection /// </summary> /// <param name="listenSocket">Socket being used to listen on</param> /// <param name="status">Connection status</param> private void OnConnection(UvStreamHandle listenSocket, int status) { UvStreamHandle acceptSocket = null; try { acceptSocket = CreateAcceptSocket(); listenSocket.Accept(acceptSocket); DispatchConnection(acceptSocket); } catch (UvException ex) when(LibuvConstants.IsConnectionReset(ex.StatusCode)) { Log.ConnectionReset("(null)"); acceptSocket?.Dispose(); } catch (UvException ex) { Log.LogError(0, ex, "Listener.OnConnection"); acceptSocket?.Dispose(); } }
private void ReadStartCallback(UvStreamHandle handle, int status) { if (status < 0) { if (status != LibuvConstants.EOF) { Thread.Loop.Libuv.Check(status, out var ex); Log.LogError(0, ex, "DispatchPipe.ReadStart"); } DispatchPipe.Dispose(); return; } if (_closed || DispatchPipe.PendingCount() == 0) { return; } var acceptSocket = CreateAcceptSocket(); try { DispatchPipe.Accept(acceptSocket); // REVIEW: This task should be tracked by the server for graceful shutdown // Today it's handled specifically for http but not for aribitrary middleware _ = HandleConnectionAsync(acceptSocket); } catch (UvException ex) when(LibuvConstants.IsConnectionReset(ex.StatusCode)) { Log.ConnectionReset("(null)"); acceptSocket.Dispose(); } catch (UvException ex) { Log.LogError(0, ex, "DispatchPipe.Accept"); acceptSocket.Dispose(); } }
private void LogWriteInfo(int status, Exception error) { if (error == null) { _log.ConnectionWriteCallback(_connectionId, status); } else { // Log connection resets at a lower (Debug) level. if (status == LibuvConstants.ECANCELED) { // Connection was aborted. } else if (LibuvConstants.IsConnectionReset(status)) { _log.ConnectionReset(_connectionId); } else { _log.ConnectionError(_connectionId, error); } } }
protected async Task HandleConnectionAsync(UvStreamHandle socket) { try { IPEndPoint remoteEndPoint = null; IPEndPoint localEndPoint = null; if (socket is UvTcpHandle tcpHandle) { try { remoteEndPoint = tcpHandle.GetPeerIPEndPoint(); localEndPoint = tcpHandle.GetSockIPEndPoint(); } catch (UvException ex) when(LibuvConstants.IsConnectionReset(ex.StatusCode)) { TransportContext.Log.ConnectionReset("(null)"); socket.Dispose(); return; } } var connection = new LibuvConnection(socket, TransportContext.Log, Thread, remoteEndPoint, localEndPoint); var middlewareTask = TransportContext.ConnectionDispatcher.OnConnection(connection); var transportTask = connection.Start(); await transportTask; await middlewareTask; connection.Dispose(); } catch (Exception ex) { TransportContext.Log.LogCritical(ex, $"Unexpected exception in {nameof(ListenerContext)}.{nameof(HandleConnectionAsync)}."); } }