From 6f93e5835ea4dcc750fb87fee54be3e334cc9747 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Tue, 25 Oct 2022 16:42:18 +0200 Subject: [PATCH 1/2] Introducing MiddlewareType and WriteToSkipMiddleware For middlewares to be able to access the fully written mail message, we need a way to execute WriteTo without the calling middleware to be handled, otherwise we end up in an infinite loop Therefore, this PR introduces the MiddlewareType and the corresponding change of the Middleware interface. We now require to return the MiddlewareType when the Type() method on the interface is called This way we can also introduce the WriteToSkipMiddleware method which takes a MiddlewareType as argument. This will allow us to use a WriteTo call with the initiating Middleware to be skipped --- msg.go | 23 +++++++++++++++++++++++ msg_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/msg.go b/msg.go index bfb521d..fb463c7 100644 --- a/msg.go +++ b/msg.go @@ -42,9 +42,14 @@ const ( errParseMailAddr = "failed to parse mail address %q: %w" ) +// MiddlewareType is the type description of the Middleware and needs to be returned +// in the Middleware interface by the Type method +type MiddlewareType string + // Middleware is an interface to define a function to apply to Msg before sending type Middleware interface { Handle(*Msg) *Msg + Type() MiddlewareType } // Msg is the mail message struct @@ -713,6 +718,24 @@ func (m *Msg) WriteTo(w io.Writer) (int64, error) { return mw.n, mw.err } +// WriteToSkipMiddleware writes the formated Msg into a give io.Writer and satisfies +// the io.WriteTo interface but will skip the given Middleware +func (m *Msg) WriteToSkipMiddleware(w io.Writer, mt MiddlewareType) (int64, error) { + var omwl, mwl []Middleware + copy(omwl, m.middlewares) + for i := range m.middlewares { + if m.middlewares[i].Type() == mt { + continue + } + mwl = append(mwl, m.middlewares[i]) + } + m.middlewares = mwl + mw := &msgWriter{w: w, c: m.charset, en: m.encoder} + mw.writeMsg(m.applyMiddlewares(m)) + m.middlewares = omwl + return mw.n, mw.err +} + // Write is an alias method to WriteTo due to compatibility reasons func (m *Msg) Write(w io.Writer) (int64, error) { return m.WriteTo(w) diff --git a/msg_test.go b/msg_test.go index 0abb724..fc164d6 100644 --- a/msg_test.go +++ b/msg_test.go @@ -189,6 +189,10 @@ func (mw uppercaseMiddleware) Handle(m *Msg) *Msg { return m } +func (mw uppercaseMiddleware) Type() MiddlewareType { + return "uppercase" +} + type encodeMiddleware struct{} func (mw encodeMiddleware) Handle(m *Msg) *Msg { @@ -200,6 +204,10 @@ func (mw encodeMiddleware) Handle(m *Msg) *Msg { return m } +func (mw encodeMiddleware) Type() MiddlewareType { + return "encode" +} + // TestNewMsgWithMiddleware tests WithMiddleware func TestNewMsgWithMiddleware(t *testing.T) { m := NewMsg() @@ -1584,6 +1592,25 @@ func TestMsg_WriteTo(t *testing.T) { } } +// TestMsg_WriteTo tests the WriteTo() method of the Msg +func TestMsg_WriteToSkipMiddleware(t *testing.T) { + m := NewMsg(WithMiddleware(encodeMiddleware{}), WithMiddleware(uppercaseMiddleware{})) + m.Subject("This is a test") + m.SetBodyString(TypeTextPlain, "Plain") + wbuf := bytes.Buffer{} + n, err := m.WriteToSkipMiddleware(&wbuf, "uppercase") + if err != nil { + t.Errorf("WriteTo() failed: %s", err) + return + } + if n != int64(wbuf.Len()) { + t.Errorf("WriteTo() failed: expected written byte length: %d, got: %d", n, wbuf.Len()) + } + if !strings.Contains(wbuf.String(), "Subject: This is @ test") { + t.Errorf("WriteToSkipMiddleware failed. Unable to find encoded subject") + } +} + // TestMsg_Write tests the Write() method of the Msg func TestMsg_Write(t *testing.T) { m := NewMsg() From 2bd6851c783818c32877e80425bf9d0040de681f Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Tue, 25 Oct 2022 16:56:40 +0200 Subject: [PATCH 2/2] Fix in the storage of the original Middleware --- msg.go | 2 +- msg_test.go | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/msg.go b/msg.go index fb463c7..d644d0a 100644 --- a/msg.go +++ b/msg.go @@ -722,7 +722,7 @@ func (m *Msg) WriteTo(w io.Writer) (int64, error) { // the io.WriteTo interface but will skip the given Middleware func (m *Msg) WriteToSkipMiddleware(w io.Writer, mt MiddlewareType) (int64, error) { var omwl, mwl []Middleware - copy(omwl, m.middlewares) + omwl = m.middlewares for i := range m.middlewares { if m.middlewares[i].Type() == mt { continue diff --git a/msg_test.go b/msg_test.go index fc164d6..7fa7a05 100644 --- a/msg_test.go +++ b/msg_test.go @@ -1600,15 +1600,28 @@ func TestMsg_WriteToSkipMiddleware(t *testing.T) { wbuf := bytes.Buffer{} n, err := m.WriteToSkipMiddleware(&wbuf, "uppercase") if err != nil { - t.Errorf("WriteTo() failed: %s", err) + t.Errorf("WriteToSkipMiddleware() failed: %s", err) return } if n != int64(wbuf.Len()) { - t.Errorf("WriteTo() failed: expected written byte length: %d, got: %d", n, wbuf.Len()) + t.Errorf("WriteToSkipMiddleware() failed: expected written byte length: %d, got: %d", n, wbuf.Len()) } if !strings.Contains(wbuf.String(), "Subject: This is @ test") { t.Errorf("WriteToSkipMiddleware failed. Unable to find encoded subject") } + + wbuf2 := bytes.Buffer{} + n, err = m.WriteTo(&wbuf2) + if err != nil { + t.Errorf("WriteTo() failed: %s", err) + return + } + if n != int64(wbuf2.Len()) { + t.Errorf("WriteTo() failed: expected written byte length: %d, got: %d", n, wbuf2.Len()) + } + if !strings.Contains(wbuf2.String(), "Subject: THIS IS @ TEST") { + t.Errorf("WriteToSkipMiddleware failed. Unable to find encoded and upperchase subject") + } } // TestMsg_Write tests the Write() method of the Msg