Skip to content

Commit 08917c9

Browse files
authored
Merge pull request #502 from wneessen/bugfix/fix-potential-nil-pointer
Refactored `SendWithSMTPClient` to improve error handling and added test cases
2 parents c68649c + 88db73d commit 08917c9

File tree

2 files changed

+103
-5
lines changed

2 files changed

+103
-5
lines changed

client.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ var (
259259

260260
// ErrDialContextFuncIsNil indicates that a required dial context function is not provided.
261261
ErrDialContextFuncIsNil = errors.New("dial context function is nil")
262+
263+
// ErrClientIsNil indicates that a required smtp client is not provided.
264+
ErrClientIsNil = errors.New("client is nil")
262265
)
263266

264267
// NewClient creates a new Client instance with the provided host and optional configuration Option functions.
@@ -1252,16 +1255,19 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
12521255
// - An error that represents the sending result, which may include multiple SendErrors if
12531256
// any occurred; otherwise, returns nil.
12541257
func (c *Client) SendWithSMTPClient(client *smtp.Client, messages ...*Msg) (returnErr error) {
1255-
escSupport := false
1256-
if client != nil {
1257-
escSupport, _ = client.Extension("ENHANCEDSTATUSCODES")
1258+
if client == nil {
1259+
return &SendError{
1260+
Reason: ErrConnCheck, errlist: []error{ErrClientIsNil}, isTemp: isTempError(ErrClientIsNil),
1261+
errcode: errorCode(ErrClientIsNil), enhancedStatusCode: enhancedStatusCode(ErrClientIsNil, false),
1262+
}
12581263
}
1264+
1265+
escSupport, _ := client.Extension("ENHANCEDSTATUSCODES")
12591266
if err := c.checkConn(client); err != nil {
1260-
returnErr = &SendError{
1267+
return &SendError{
12611268
Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err),
12621269
errcode: errorCode(err), enhancedStatusCode: enhancedStatusCode(err, escSupport),
12631270
}
1264-
return returnErr
12651271
}
12661272

12671273
var errs []error

client_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3755,6 +3755,98 @@ func TestClient_DialWithContextNewVersionsOnly(t *testing.T) {
37553755
})
37563756
}
37573757

3758+
func TestClient_SendWithSMTPClient(t *testing.T) {
3759+
message := testMessage(t)
3760+
3761+
t.Run("sending with normal client should succeed", func(t *testing.T) {
3762+
ctx, cancel := context.WithCancel(context.Background())
3763+
defer cancel()
3764+
PortAdder.Add(1)
3765+
serverPort := int(TestServerPortBase + PortAdder.Load())
3766+
featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
3767+
go func() {
3768+
if err := simpleSMTPServer(ctx, t, &serverProps{FeatureSet: featureSet, ListenPort: serverPort}); err != nil {
3769+
t.Errorf("failed to start test server: %s", err)
3770+
return
3771+
}
3772+
}()
3773+
time.Sleep(time.Millisecond * 30)
3774+
3775+
ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500)
3776+
t.Cleanup(cancelDial)
3777+
3778+
client, err := NewClient(DefaultHost, WithPort(serverPort), WithTLSPolicy(NoTLS))
3779+
if err != nil {
3780+
t.Fatalf("failed to create new client: %s", err)
3781+
}
3782+
if err = client.DialWithContext(ctxDial); err != nil {
3783+
var netErr net.Error
3784+
if errors.As(err, &netErr) && netErr.Timeout() {
3785+
t.Skip("failed to connect to the test server due to timeout")
3786+
}
3787+
t.Fatalf("failed to connect to test server: %s", err)
3788+
}
3789+
t.Cleanup(func() {
3790+
if err := client.Close(); err != nil {
3791+
t.Errorf("failed to close client: %s", err)
3792+
}
3793+
})
3794+
if err = client.SendWithSMTPClient(client.smtpClient, message); err != nil {
3795+
t.Errorf("failed to deliver message via smtp client: %s", err)
3796+
}
3797+
})
3798+
t.Run("sending should return a send error on connection test", func(t *testing.T) {
3799+
ctx, cancel := context.WithCancel(context.Background())
3800+
defer cancel()
3801+
PortAdder.Add(1)
3802+
serverPort := int(TestServerPortBase + PortAdder.Load())
3803+
featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
3804+
go func() {
3805+
if err := simpleSMTPServer(ctx, t, &serverProps{
3806+
FeatureSet: featureSet,
3807+
ListenPort: serverPort,
3808+
FailOnNoop: true,
3809+
}); err != nil {
3810+
t.Errorf("failed to start test server: %s", err)
3811+
return
3812+
}
3813+
}()
3814+
time.Sleep(time.Millisecond * 30)
3815+
3816+
ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500)
3817+
t.Cleanup(cancelDial)
3818+
3819+
client, err := NewClient(DefaultHost, WithPort(serverPort), WithTLSPolicy(NoTLS))
3820+
if err != nil {
3821+
t.Fatalf("failed to create new client: %s", err)
3822+
}
3823+
if err = client.DialWithContext(ctxDial); err != nil {
3824+
var netErr net.Error
3825+
if errors.As(err, &netErr) && netErr.Timeout() {
3826+
t.Skip("failed to connect to the test server due to timeout")
3827+
}
3828+
t.Fatalf("failed to connect to test server: %s", err)
3829+
}
3830+
t.Cleanup(func() {
3831+
if err := client.Close(); err != nil {
3832+
t.Errorf("failed to close client: %s", err)
3833+
}
3834+
})
3835+
if err = client.SendWithSMTPClient(client.smtpClient, message); err == nil {
3836+
t.Errorf("expected delivery to fail on connection check, but it didn't")
3837+
}
3838+
})
3839+
t.Run("sending with nil client should fail", func(t *testing.T) {
3840+
client, err := NewClient(DefaultHost)
3841+
if err != nil {
3842+
t.Fatalf("failed to create new client: %s", err)
3843+
}
3844+
if err = client.SendWithSMTPClient(nil, message); err == nil {
3845+
t.Errorf("expected delivery to fail with nil client, but it didn't")
3846+
}
3847+
})
3848+
}
3849+
37583850
func TestClient_checkConn(t *testing.T) {
37593851
t.Run("connection is alive", func(t *testing.T) {
37603852
ctx, cancel := context.WithCancel(context.Background())

0 commit comments

Comments
 (0)