From 7eb5a54224d645f04f0c28c260d2ae14b14c8217 Mon Sep 17 00:00:00 2001 From: Zhang Huangbin Date: Mon, 22 Aug 2022 10:02:14 +0800 Subject: [PATCH 1/3] feat: Implement Close. fixes #20 --- server.go | 24 ++++++++++++++++++++++++ session.go | 3 +-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 378a1d3..7cf749e 100644 --- a/server.go +++ b/server.go @@ -54,6 +54,26 @@ type Milter interface { // should be reset to prior to the Helo callback. Connection data should be // preserved. Abort(m *Modifier) error + + // Close is called once at the end of each connection. + // + // - Close may be called "out-of-order", i.e. before even the + // Connect is called. After a connection is established by the + // MTA to the filter, if the MTA decides this connection's + // traffic will be discarded, no data will be passed to the + // filter from the MTA until the client closes down. At that time, + // Close is called. It can therefore be the only callback ever + // used for a given connection, and developers should anticipate + // this possibility when crafting their Close code. In particular, + // it is incorrect to assume the private context pointer will be + // something other than NULL in this callback. + // - Close is called on close even if the previous mail + // transaction was aborted. + // - Close is responsible for freeing any resources allocated on + // a per-connection basis. + // - Since the connection is already closing, the return value is + // currently ignored. + Close(m *Modifier) (Response, error) } // NoOpMilter is a dummy Milter implementation that does nothing. @@ -97,6 +117,10 @@ func (NoOpMilter) Abort(m *Modifier) error { return nil } +func (NoOpMilter) Close(m *Modifier) (Response, error) { + return RespContinue, nil +} + // Server is a milter server. type Server struct { NewMilter func() Milter diff --git a/session.go b/session.go index 3d232cb..e226043 100644 --- a/session.go +++ b/session.go @@ -210,8 +210,7 @@ func (m *milterSession) Process(msg *Message) (Response, error) { case CodeQuit: // client requested session close - return nil, errCloseSession - + return m.backend.Close(newModifier(m)) case CodeRcpt: // envelope to address to := readCString(msg.Data) From ef9b21c73dd759825485eff7d265c04aa0524588 Mon Sep 17 00:00:00 2001 From: Zhang Huangbin Date: Mon, 22 Aug 2022 10:11:22 +0800 Subject: [PATCH 2/3] Implement Close in client. --- client.go | 2 +- client_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index 1602d4e..4c7f0da 100644 --- a/client.go +++ b/client.go @@ -646,7 +646,7 @@ func (s *ClientSession) Abort() error { // Close releases resources associated with the session. // -// If there a milter sequence in progress - it is aborted. +// If there's a milter sequence in progress - it is aborted. func (s *ClientSession) Close() error { if s.needAbort { _ = s.Abort() diff --git a/client_test.go b/client_test.go index 7822374..db0032e 100644 --- a/client_test.go +++ b/client_test.go @@ -51,6 +51,10 @@ type MockMilter struct { AbortMod func(m *Modifier) AbortErr error + CloseResp Response + CloseMod func(m *Modifier) + CloseErr error + // Info collected during calls. Host string Family string @@ -137,6 +141,13 @@ func (mm *MockMilter) Abort(m *Modifier) error { return mm.AbortErr } +func (mm *MockMilter) Close(m *Modifier) (Response, error) { + if mm.CloseMod != nil { + mm.CloseMod(m) + } + return mm.CloseResp, mm.CloseErr +} + func TestMilterClient_UsualFlow(t *testing.T) { mm := MockMilter{ ConnResp: RespContinue, @@ -152,6 +163,7 @@ func TestMilterClient_UsualFlow(t *testing.T) { m.ChangeHeader(1, "Subject", "***SPAM***") m.Quarantine("very bad message") }, + CloseResp: RespContinue, } s := Server{ NewMilter: func() Milter { From 9b9b6c684f7bb225b871bc9e180b3efb75665bc1 Mon Sep 17 00:00:00 2001 From: Zhang Huangbin Date: Tue, 23 Aug 2022 11:00:21 +0800 Subject: [PATCH 3/3] Don't return any value in Close(). --- client_test.go | 12 ++---------- server.go | 6 +++--- session.go | 3 ++- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/client_test.go b/client_test.go index db0032e..29fbdcc 100644 --- a/client_test.go +++ b/client_test.go @@ -51,10 +51,6 @@ type MockMilter struct { AbortMod func(m *Modifier) AbortErr error - CloseResp Response - CloseMod func(m *Modifier) - CloseErr error - // Info collected during calls. Host string Family string @@ -141,11 +137,8 @@ func (mm *MockMilter) Abort(m *Modifier) error { return mm.AbortErr } -func (mm *MockMilter) Close(m *Modifier) (Response, error) { - if mm.CloseMod != nil { - mm.CloseMod(m) - } - return mm.CloseResp, mm.CloseErr +func (mm *MockMilter) Close(m *Modifier) { + return } func TestMilterClient_UsualFlow(t *testing.T) { @@ -163,7 +156,6 @@ func TestMilterClient_UsualFlow(t *testing.T) { m.ChangeHeader(1, "Subject", "***SPAM***") m.Quarantine("very bad message") }, - CloseResp: RespContinue, } s := Server{ NewMilter: func() Milter { diff --git a/server.go b/server.go index 7cf749e..54cc5be 100644 --- a/server.go +++ b/server.go @@ -73,7 +73,7 @@ type Milter interface { // a per-connection basis. // - Since the connection is already closing, the return value is // currently ignored. - Close(m *Modifier) (Response, error) + Close(m *Modifier) } // NoOpMilter is a dummy Milter implementation that does nothing. @@ -117,8 +117,8 @@ func (NoOpMilter) Abort(m *Modifier) error { return nil } -func (NoOpMilter) Close(m *Modifier) (Response, error) { - return RespContinue, nil +func (NoOpMilter) Close(m *Modifier) { + return } // Server is a milter server. diff --git a/session.go b/session.go index e226043..f793ff1 100644 --- a/session.go +++ b/session.go @@ -210,7 +210,8 @@ func (m *milterSession) Process(msg *Message) (Response, error) { case CodeQuit: // client requested session close - return m.backend.Close(newModifier(m)) + m.backend.Close(newModifier(m)) + return nil, errCloseSession case CodeRcpt: // envelope to address to := readCString(msg.Data)