private async Task <MySqlSession> CreateSessionAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) { var connectTimeout = m_connectionSettings.ConnectionTimeout == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(checked ((int)m_connectionSettings.ConnectionTimeout)); using (var timeoutSource = new CancellationTokenSource(connectTimeout)) using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token)) { try { // get existing session from the pool if possible if (m_connectionSettings.Pooling) { var pool = ConnectionPool.GetPool(m_connectionSettings); // this returns an open session return(await pool.GetSessionAsync(ioBehavior, linkedSource.Token).ConfigureAwait(false)); } else { var session = new MySqlSession(); await session.ConnectAsync(m_connectionSettings, ioBehavior, linkedSource.Token).ConfigureAwait(false); return(session); } } catch (OperationCanceledException ex) when(timeoutSource.IsCancellationRequested) { throw new MySqlException("Connect Timeout expired.", ex); } } }
public async Task <MySqlSession> GetSessionAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // wait for an open slot if (ioBehavior == IOBehavior.Asynchronous) { await m_session_semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } else { m_session_semaphore.Wait(cancellationToken); } try { MySqlSession session; // check for a pooled session if (m_sessions.TryDequeue(out session)) { if (session.PoolGeneration != m_generation || !await session.TryPingAsync(ioBehavior, cancellationToken).ConfigureAwait(false)) { // session is either old or cannot communicate with the server await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false); } else { // session is valid, reset if supported if (m_resetConnections) { await session.ResetConnectionAsync(m_userId, m_password, m_database, ioBehavior, cancellationToken).ConfigureAwait(false); } // pooled session is ready to be used; return it return(session); } } session = new MySqlSession(this, m_generation); await session.ConnectAsync(m_servers, m_port, m_userId, m_password, m_database, ioBehavior, cancellationToken).ConfigureAwait(false); return(session); } catch { m_session_semaphore.Release(); throw; } }
public async Task <MySqlSession> GetSessionAsync(MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // if all sessions are used, see if any have been leaked and can be recovered // check at most once per second (although this isn't enforced via a mutex so multiple threads might block // on the lock in RecoverLeakedSessions in high-concurrency situations if (m_sessionSemaphore.CurrentCount == 0 && unchecked (((uint)Environment.TickCount) - m_lastRecoveryTime) >= 1000u) { RecoverLeakedSessions(); } // wait for an open slot (until the cancellationToken is cancelled, which is typically due to timeout) if (ioBehavior == IOBehavior.Asynchronous) { await m_sessionSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } else { m_sessionSemaphore.Wait(cancellationToken); } try { // check for a waiting session MySqlSession session = null; lock (m_sessions) { if (m_sessions.Count > 0) { session = m_sessions.First.Value; m_sessions.RemoveFirst(); } } if (session != null) { bool reuseSession; if (session.PoolGeneration != m_generation) { reuseSession = false; } else { if (m_connectionSettings.ConnectionReset) { reuseSession = await session.TryResetConnectionAsync(m_connectionSettings, ioBehavior, cancellationToken).ConfigureAwait(false); } else { reuseSession = await session.TryPingAsync(ioBehavior, cancellationToken).ConfigureAwait(false); } } if (!reuseSession) { // session is either old or cannot communicate with the server await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false); } else { // pooled session is ready to be used; return it session.OwningConnection = new WeakReference <MySqlConnection>(connection); lock (m_leasedSessions) m_leasedSessions.Add(session.Id, session); return(session); } } // create a new session session = new MySqlSession(this, m_generation, Interlocked.Increment(ref m_lastId)); await session.ConnectAsync(m_connectionSettings, ioBehavior, cancellationToken).ConfigureAwait(false); session.OwningConnection = new WeakReference <MySqlConnection>(connection); lock (m_leasedSessions) m_leasedSessions.Add(session.Id, session); return(session); } catch { m_sessionSemaphore.Release(); throw; } }
public override async Task OpenAsync(CancellationToken cancellationToken) { VerifyNotDisposed(); if (State != ConnectionState.Closed) { throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State)); } #if !NETSTANDARD1_3 if (System.Transactions.Transaction.Current != null) { throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead."); } #endif SetState(ConnectionState.Connecting); bool success = false; try { // get existing session from the pool if possible var pool = ConnectionPool.GetPool(m_connectionStringBuilder); m_session = pool == null ? null : await pool.TryGetSessionAsync(cancellationToken).ConfigureAwait(false); if (m_session != null) { // test that session is still valid and (optionally) reset it if (!await TryPingAsync(m_session, cancellationToken).ConfigureAwait(false)) { Utility.Dispose(ref m_session); } else if (m_connectionStringBuilder.ConnectionReset) { await ResetConnectionAsync(cancellationToken).ConfigureAwait(false); } } if (m_session == null) { m_session = new MySqlSession(pool); var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false); if (!connected) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); m_session.AuthPluginData = initialHandshake.AuthPluginData; var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database); payload = new PayloadData(new ArraySegment <byte>(response)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); // TODO: Check success } m_hasBeenOpened = true; SetState(ConnectionState.Open); success = true; } catch (MySqlException) { SetState(ConnectionState.Closed); throw; } catch (SocketException ex) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex); } finally { if (!success) { Utility.Dispose(ref m_session); } } }
public override async Task OpenAsync(CancellationToken cancellationToken) { VerifyNotDisposed(); if (State != ConnectionState.Closed) throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State)); #if !NETSTANDARD1_3 if (System.Transactions.Transaction.Current != null) throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead."); #endif if (m_connectionStringBuilder.UseCompression) throw new NotSupportedException("Compression not supported."); SetState(ConnectionState.Connecting); bool success = false; try { var pool = ConnectionPool.GetPool(m_connectionStringBuilder); m_session = pool?.TryGetSession(); if (m_session == null) { m_session = new MySqlSession(pool); var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int) m_connectionStringBuilder.Port).ConfigureAwait(false); if (!connected) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); m_session.AuthPluginData = initialHandshake.AuthPluginData; var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database); payload = new PayloadData(new ArraySegment<byte>(response)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); // TODO: Check success } else if (m_connectionStringBuilder.ConnectionReset) { if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false); var payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } else { // optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL) var hashedPassword = AuthenticationUtility.HashPassword(m_session.AuthPluginData, 0, m_connectionStringBuilder.Password); var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, hashedPassword, m_database); await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { // if the server didn't support the hashed password; rehash with the new challenge var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); if (switchRequest.Name != "mysql_native_password") throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password); payload = new PayloadData(new ArraySegment<byte>(hashedPassword)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); } } m_hasBeenOpened = true; SetState(ConnectionState.Open); success = true; } catch (MySqlException) { SetState(ConnectionState.Closed); throw; } catch (SocketException ex) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex); } finally { if (!success) Utility.Dispose(ref m_session); } }
public override async Task OpenAsync(CancellationToken cancellationToken) { VerifyNotDisposed(); if (State != ConnectionState.Closed) { throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State)); } #if !NETSTANDARD1_3 if (System.Transactions.Transaction.Current != null) { throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead."); } #endif if (m_connectionStringBuilder.UseCompression) { throw new NotSupportedException("Compression not supported."); } SetState(ConnectionState.Connecting); bool success = false; try { var pool = ConnectionPool.GetPool(m_connectionStringBuilder); m_session = pool?.TryGetSession(); if (m_session == null) { m_session = new MySqlSession(pool); var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false); if (!connected) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database); payload = new PayloadData(new ArraySegment <byte>(response)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); // TODO: Check success } else if (m_connectionStringBuilder.ConnectionReset) { if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false); var payload = await m_session.ReceiveReplyAsync(cancellationToken); OkPayload.Create(payload); } else { // MySQL doesn't appear to accept a replayed hashed password (using the challenge from the initial handshake), so just send zeroes // and expect to get a new challenge var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, new byte[20], m_database); await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); if (switchRequest.Name != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } var hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password); payload = new PayloadData(new ArraySegment <byte>(hashedPassword)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } } m_hasBeenOpened = true; SetState(ConnectionState.Open); success = true; } catch (MySqlException) { SetState(ConnectionState.Closed); throw; } catch (SocketException ex) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex); } finally { if (!success) { Utility.Dispose(ref m_session); } } }