_message = message;
message.Send(writer, DeliveryMethod != SmtpDeliveryMethod.Network, allowUnicode);
writer.Close();
- _transport.ReleaseConnection();
//throw if we couldn't send to any of the recipients
if (DeliveryMethod == SmtpDeliveryMethod.Network && recipientException != null)
if (_writer != null)
_writer.Close();
- _transport.ReleaseConnection();
AsyncCompletedEventArgs eventArgs = new AsyncCompletedEventArgs(null, false, _asyncOp.UserSuppliedState);
InCall = false;
_asyncOp.PostOperationCompleted(_onSendCompletedDelegate, eventArgs);
exception = se;
}
}
- _transport.ReleaseConnection();
}
}
finally
_cancelled = true;
Abort();
}
-
- if ((_transport != null))
- {
- _transport.ReleaseConnection();
- }
-
- if (_timer != null)
+ else
{
- _timer.Dispose();
+ _transport?.ReleaseConnection();
}
-
+ _timer?.Dispose();
_disposed = true;
}
}
}
}
+ internal static class QuitCommand
+ {
+ private static void PrepareCommand(SmtpConnection conn)
+ {
+ if (conn.IsStreamOpen)
+ {
+ throw new InvalidOperationException(SR.SmtpDataStreamOpen);
+ }
+
+ conn.BufferBuilder.Append(SmtpCommands.Quit);
+ }
+
+ internal static void Send(SmtpConnection conn)
+ {
+ PrepareCommand(conn);
+
+ // We simply flush and don't read the response
+ // to avoid blocking call that will impact users
+ // that are using async api, since this code
+ // will run on Dispose()
+ conn.Flush();
+ }
+ }
internal static class SmtpCommands
{
_bufferBuilder.Reset();
}
- internal void ReleaseConnection()
+ private void ShutdownConnection(bool isAbort)
{
if (!_isClosed)
{
{
if (!_isClosed && _tcpClient != null)
{
- //free cbt buffer
- if (_channelBindingToken != null)
+ try
+ {
+ try
+ {
+ if (isAbort)
+ {
+ // Must destroy manually since sending a QUIT here might not be
+ // interpreted correctly by the server if it's in the middle of a
+ // DATA command or some similar situation. This may send a RST
+ // but this is ok in this situation. Do not reuse this connection
+ _tcpClient.LingerState = new LingerOption(true, 0);
+ }
+ else
+ {
+ // Gracefully close the transmission channel
+ _tcpClient.Client.Blocking = false;
+ QuitCommand.Send(this);
+ }
+ }
+ finally
+ {
+ //free cbt buffer
+ _channelBindingToken?.Close();
+ _networkStream?.Close();
+ _tcpClient.Dispose();
+ }
+ }
+ catch (IOException)
{
- _channelBindingToken.Close();
+ // Network failure
+ }
+ catch (ObjectDisposedException)
+ {
+ // See https://github.com/dotnet/corefx/issues/40711, and potentially
+ // catch additional exception types here if need demonstrates.
}
-
- _networkStream?.Close();
- _tcpClient.Dispose();
}
_isClosed = true;
_isConnected = false;
}
+ internal void ReleaseConnection()
+ {
+ ShutdownConnection(false);
+ }
+
internal void Abort()
{
- if (!_isClosed)
- {
- try
- {
- lock (this)
- {
- if (!_isClosed && _tcpClient != null)
- {
- _channelBindingToken?.Close();
-
- // Must destroy manually since sending a QUIT here might not be
- // interpreted correctly by the server if it's in the middle of a
- // DATA command or some similar situation. This may send a RST
- // but this is ok in this situation. Do not reuse this connection
- _tcpClient.LingerState = new LingerOption(true, 0);
- _networkStream?.Close();
- _tcpClient.Dispose();
- }
- _isClosed = true;
- }
- }
- catch (ObjectDisposedException)
- {
- // See https://github.com/dotnet/runtime/issues/30732, and potentially
- // catch additional exception types here if need demonstrates.
- }
- }
- _isConnected = false;
+ ShutdownConnection(true);
}
internal void GetConnection(string host, int port)
internal void ReleaseConnection()
{
- if (_connection != null)
- {
- _connection.ReleaseConnection();
- }
+ _connection?.ReleaseConnection();
}
internal void Abort()
}
private static string GetClientDomain() => IPGlobalProperties.GetIPGlobalProperties().HostName.Trim().ToLower();
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task SendMail_SendQUITOnDispose(bool asyncSend)
+ {
+ bool quitMessageReceived = false;
+ using ManualResetEventSlim quitReceived = new ManualResetEventSlim();
+ using var server = new LoopbackSmtpServer();
+ server.OnQuitReceived += _ =>
+ {
+ quitMessageReceived = true;
+ quitReceived.Set();
+ };
+
+ using (SmtpClient client = server.CreateClient())
+ {
+ client.Credentials = new NetworkCredential("Foo", "Bar");
+ MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", "hello", "howdydoo");
+ if (asyncSend)
+ {
+ await client.SendMailAsync(msg).TimeoutAfter((int)TimeSpan.FromSeconds(30).TotalMilliseconds);
+ }
+ else
+ {
+ client.Send(msg);
+ }
+ Assert.False(quitMessageReceived, "QUIT received");
+ }
+
+ // There is a latency between send/receive.
+ quitReceived.Wait(TimeSpan.FromSeconds(30));
+ Assert.True(quitMessageReceived, "QUIT message not received");
+ }
}
}