public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; try { EpoxyNetworkStream epoxyStream = await EpoxyNetworkStream.MakeAsync( socketFunc : () => ConnectClientSocketAsync(endpoint), streamFunc : socket => EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger), timeoutConfig : timeoutConfig, logger : logger); // TODO: keep these in some master collection for shutdown var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); await connection.StartAsync(); return(connection); } catch (Exception ex) { logger.Site().Error(ex, "Failed to start Epoxy client connection to '{0}'", endpoint); throw; } }
async Task StartClientConnectionAsync(Task <Socket> socketTask) { // If this throws, it will have cleaned up before getting here, so we don't need to // handle cleanup. EpoxyNetworkStream epoxyStream = await EpoxyNetworkStream.MakeAsync( socketFunc : () => socketTask, streamFunc : socket => EpoxyNetworkStream.MakeServerStreamAsync(socket, tlsConfig, logger), timeoutConfig : timeoutConfig, logger : logger); // We now have a completely established EpoxyNetworkStream that will eventually need to // be shutdown. None of the code between here and adding the connection to our collection // will throw (for network I/O reasons), so we'll safely save the connection. (The // intervening code may throw for things like OOM. If it does, we've got bigger problems // that we likely won't be able to recover from anyway. Ignoring that contingency for // now.) var connection = EpoxyConnection.MakeServerConnection( parentTransport, this, serviceHost, epoxyStream, logger, metrics); lock (connectionsLock) { connections.Add(connection); } logger.Site().Debug("Setup server-side connection for {0}. Starting Epoxy handshake.", connection.RemoteEndPoint); await connection.StartAsync(); }
private async Task AcceptAsync(CancellationToken t) { logger.Site().Information("Accepting connections on {0}", ListenEndpoint); while (!t.IsCancellationRequested) { Socket socket = null; EpoxyNetworkStream epoxyStream = null; try { socket = await listener.AcceptSocketAsync(); logger.Site().Debug("Accepted connection from {0}.", socket.RemoteEndPoint); EpoxyTransport.ConfigureSocketKeepAlive(socket, timeoutConfig, logger); epoxyStream = await EpoxyNetworkStream.MakeServerStreamAsync(socket, tlsConfig, logger); socket = null; // epoxyStream now owns the socket var connection = EpoxyConnection.MakeServerConnection( parentTransport, this, serviceHost, epoxyStream, logger, metrics); // connection now owns the EpoxyNetworkStream epoxyStream = null; lock (connectionsLock) { connections.Add(connection); } await connection.StartAsync(); logger.Site().Debug("Started server-side connection for {0}", connection.RemoteEndPoint); } catch (AuthenticationException ex) { logger.Site().Error(ex, "Failed to authenticate remote connection from {0}", socket?.RemoteEndPoint); ShutdownSocketSafe(socket, epoxyStream); } catch (SocketException ex) { logger.Site().Error(ex, "Accept failed with error {0}.", ex.SocketErrorCode); ShutdownSocketSafe(socket, epoxyStream); } catch (ObjectDisposedException) { ShutdownSocketSafe(socket, epoxyStream); } } logger.Site().Information("Shutting down connection on {0}", ListenEndpoint); }
public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); Socket socket = await ConnectClientSocketAsync(endpoint); // For MakeClientStreamAsync, null TLS config means insecure EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; var epoxyStream = await EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger); // TODO: keep these in some master collection for shutdown var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); await connection.StartAsync(); return(connection); }
internal static EpoxyConnection MakeServerConnection( EpoxyTransport parentTransport, EpoxyListener parentListener, ServiceHost serviceHost, EpoxyNetworkStream networkStream, Logger logger, Metrics metrics) { return(new EpoxyConnection( ConnectionType.Server, parentTransport, parentListener, serviceHost, networkStream, logger, metrics)); }
internal static EpoxyConnection MakeClientConnection( EpoxyTransport parentTransport, EpoxyNetworkStream networkStream, Logger logger, Metrics metrics) { const EpoxyListener parentListener = null; return(new EpoxyConnection( ConnectionType.Client, parentTransport, parentListener, new ServiceHost(logger), networkStream, logger, metrics)); }
private void ShutdownSocketSafe(Socket socket, EpoxyNetworkStream epoxyStream) { if (epoxyStream != null) { epoxyStream.Shutdown(); // epoxyStream owns the socket, so we shouldn't try to shutdown socket = null; } try { socket?.Shutdown(SocketShutdown.Both); socket?.Close(); } catch (SocketException ex) { // We tried to cleanly shutdown the socket, oh well. logger.Site().Debug(ex, "Exception encountered when shutting down a socket."); } }
public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; try { EpoxyNetworkStream epoxyStream = await EpoxyNetworkStream.MakeAsync( socketFunc : () => ConnectClientSocketAsync(endpoint), streamFunc : socket => EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger), timeoutConfig : timeoutConfig, logger : logger); var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); try { connections.Add(connection); } catch (InvalidOperationException) { await connection.StopAsync(); throw new InvalidOperationException("This EpoxyTransport has been stopped already."); } await connection.StartAsync(); return(connection); } catch (Exception ex) { logger.Site().Error(ex, "Failed to start Epoxy client connection to '{0}'", endpoint); throw; } }
private EpoxyConnection( ConnectionType connectionType, EpoxyTransport parentTransport, EpoxyListener parentListener, ServiceHost serviceHost, EpoxyNetworkStream networkStream, Logger logger, Metrics metrics) { Debug.Assert(parentTransport != null); Debug.Assert(connectionType != ConnectionType.Server || parentListener != null, "Server connections must have a listener"); Debug.Assert(serviceHost != null); Debug.Assert(networkStream != null); this.connectionType = connectionType; this.parentTransport = parentTransport; this.parentListener = parentListener; this.serviceHost = serviceHost; this.networkStream = networkStream; responseMap = new ResponseMap(); state = State.Created; startTask = new TaskCompletionSource <bool>(); stopTask = new TaskCompletionSource <bool>(); shutdownTokenSource = new CancellationTokenSource(); // start at -1 or 0 so the first conversation ID is 1 or 2. prevConversationId = (connectionType == ConnectionType.Client) ? -1 : 0; ConnectionMetrics.local_endpoint = LocalEndPoint.ToString(); ConnectionMetrics.remote_endpoint = RemoteEndPoint.ToString(); this.logger = logger; this.metrics = metrics; }
async Task StartClientConnectionAsync(Socket socket) { EpoxyNetworkStream epoxyStream = null; try { EpoxyTransport.ConfigureSocketKeepAlive(socket, timeoutConfig, logger); epoxyStream = await EpoxyNetworkStream.MakeServerStreamAsync(socket, tlsConfig, logger); socket = null; // epoxyStream now owns the socket var connection = EpoxyConnection.MakeServerConnection( parentTransport, this, serviceHost, epoxyStream, logger, metrics); // connection now owns the EpoxyNetworkStream epoxyStream = null; lock (connectionsLock) { connections.Add(connection); } logger.Site().Debug("Setup server-side connection for {0}. Starting Epoxy handshake.", connection.RemoteEndPoint); await connection.StartAsync(); } catch (Exception) { ShutdownSocketSafe(socket, epoxyStream); throw; } }