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
This commit is contained in:
Winni Neessen 2022-10-25 16:42:18 +02:00
parent a44383eed2
commit 6f93e5835e
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
2 changed files with 50 additions and 0 deletions

23
msg.go
View file

@ -42,9 +42,14 @@ const (
errParseMailAddr = "failed to parse mail address %q: %w" 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 // Middleware is an interface to define a function to apply to Msg before sending
type Middleware interface { type Middleware interface {
Handle(*Msg) *Msg Handle(*Msg) *Msg
Type() MiddlewareType
} }
// Msg is the mail message struct // Msg is the mail message struct
@ -713,6 +718,24 @@ func (m *Msg) WriteTo(w io.Writer) (int64, error) {
return mw.n, mw.err 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 // Write is an alias method to WriteTo due to compatibility reasons
func (m *Msg) Write(w io.Writer) (int64, error) { func (m *Msg) Write(w io.Writer) (int64, error) {
return m.WriteTo(w) return m.WriteTo(w)

View file

@ -189,6 +189,10 @@ func (mw uppercaseMiddleware) Handle(m *Msg) *Msg {
return m return m
} }
func (mw uppercaseMiddleware) Type() MiddlewareType {
return "uppercase"
}
type encodeMiddleware struct{} type encodeMiddleware struct{}
func (mw encodeMiddleware) Handle(m *Msg) *Msg { func (mw encodeMiddleware) Handle(m *Msg) *Msg {
@ -200,6 +204,10 @@ func (mw encodeMiddleware) Handle(m *Msg) *Msg {
return m return m
} }
func (mw encodeMiddleware) Type() MiddlewareType {
return "encode"
}
// TestNewMsgWithMiddleware tests WithMiddleware // TestNewMsgWithMiddleware tests WithMiddleware
func TestNewMsgWithMiddleware(t *testing.T) { func TestNewMsgWithMiddleware(t *testing.T) {
m := NewMsg() 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 // TestMsg_Write tests the Write() method of the Msg
func TestMsg_Write(t *testing.T) { func TestMsg_Write(t *testing.T) {
m := NewMsg() m := NewMsg()