smtpmock
is lightweight configurable multithreaded fake SMTP server written in Go. It meets the minimum requirements specified by RFC 2821 & RFC 5321. Allows to mimic any SMTP server behavior for your test environment and even more 🚀
Table of Contents
Features
- Configurable multithreaded RFC compatible SMTP server
- Implements the minimum command set, responds to commands and adds a valid received header to messages as specified in RFC 2821 & RFC 5321
- Ability to configure behavior for each SMTP command
- Comes with default settings out of the box, configure only what you need
- Ability to override previous SMTP commands
- Fail fast scenario (ability to close client session for case when command was inconsistent or failed)
- Multiple receivers (ability to configure multiple
RCPT TO
commands receiving during one session) - Multiple message receiving (ability to configure multiple message receiving during one session)
- Mock-server activity logger
- Ability to do graceful/force shutdown of SMTP mock server
- No authentication support
- Zero runtime dependencies
- Ability to access to server messages
- Simple and intuitive DSL
- Ability to run server as binary with command line arguments
Requirements
Golang 1.15+
Installation
Install smtpmock
:
go get github.com/mocktools/go-smtp-mock/v2
go install github.com/mocktools/go-smtp-mock/v2
Import smtpmock
dependency into your code:
package main
import smtpmock "github.com/mocktools/go-smtp-mock/v2"
Usage
Inside of Golang ecosystem
You have to create your SMTP mock server using smtpmock.New()
and smtpmock.ConfigurationAttr{}
to start interaction with it.
Configuring
smtpmock
is SMTP server for test environment with configurable behavior. It comes with default settings out of the box. But you can override any default behavior if you need.
smtpmock.ConfigurationAttr{
HostAddress: "[::]",
PortNumber: 2525,
LogToStdout: true,
LogServerActivity: true,
SessionTimeout: 42,
ShutdownTimeout: 5,
IsCmdFailFast: true,
MultipleRcptto: true,
MultipleMessageReceiving: true,
BlacklistedHeloDomains: []string{"example1.com", "example2.com", "localhost"},
BlacklistedMailfromEmails: []string{"bot@olo.com", "robot@molo.com"},
BlacklistedRcpttoEmails: []string{"blacklisted@olo.com", "blacklisted@molo.com"},
NotRegisteredEmails: []string{"nobody@olo.com", "non-existent@email.com"},
ResponseDelayHelo: 2,
ResponseDelayMailfrom: 2,
ResponseDelayRcptto: 2,
ResponseDelayData: 2,
ResponseDelayMessage: 2,
ResponseDelayRset: 2,
ResponseDelayNoop: 2,
ResponseDelayQuit: 2,
MsgSizeLimit: 5,
MsgGreeting: "msgGreeting",
MsgInvalidCmd: "msgInvalidCmd",
MsgInvalidCmdHeloSequence: "msgInvalidCmdHeloSequence",
MsgInvalidCmdHeloArg: "msgInvalidCmdHeloArg",
MsgHeloBlacklistedDomain: "msgHeloBlacklistedDomain",
MsgHeloReceived: "msgHeloReceived",
MsgInvalidCmdMailfromSequence: "msgInvalidCmdMailfromSequence",
MsgInvalidCmdMailfromArg: "msgInvalidCmdMailfromArg",
MsgMailfromBlacklistedEmail: "msgMailfromBlacklistedEmail",
MsgMailfromReceived: "msgMailfromReceived",
MsgInvalidCmdRcpttoSequence: "msgInvalidCmdRcpttoSequence",
MsgInvalidCmdRcpttoArg: "msgInvalidCmdRcpttoArg",
MsgRcpttoNotRegisteredEmail: "msgRcpttoNotRegisteredEmail",
MsgRcpttoBlacklistedEmail: "msgRcpttoBlacklistedEmail",
MsgRcpttoReceived: "msgRcpttoReceived",
MsgInvalidCmdDataSequence: "msgInvalidCmdDataSequence",
MsgDataReceived: "msgDataReceived",
MsgMsgSizeIsTooBig: "msgMsgSizeIsTooBig",
MsgMsgReceived: "msgMsgReceived",
MsgInvalidCmdRsetSequence: "msgInvalidCmdRsetSequence",
MsgInvalidCmdRsetArg: "msgInvalidCmdRsetArg",
MsgRsetReceived: "msgRsetReceived",
MsgNoopReceived: "msgNoopReceived",
MsgQuitCmd: "msgQuitCmd",
}
Manipulation with server
package main
import (
"fmt"
"net"
"net/smtp"
smtpmock "github.com/mocktools/go-smtp-mock/v2"
)
func main() {
server := smtpmock.New(smtpmock.ConfigurationAttr{
LogToStdout: true,
LogServerActivity: true,
})
if err := server.Start(); err != nil {
fmt.Println(err)
}
hostAddress, portNumber := "127.0.0.1", server.PortNumber()
address := fmt.Sprintf("%s:%d", hostAddress, portNumber)
timeout := time.Duration(2) * time.Second
connection, _ := net.DialTimeout("tcp", address, timeout)
client, _ := smtp.NewClient(connection, hostAddress)
client.Hello("example.com")
client.Quit()
client.Close()
server.Messages()
server.MessagesAndPurge()
if err := server.Stop(); err != nil {
fmt.Println(err)
}
}
Code from example above will produce next output to stdout:
INFO: 2021/11/30 22:07:30.554827 SMTP mock server started on port: 2525
INFO: 2021/11/30 22:07:30.554961 SMTP session started
INFO: 2021/11/30 22:07:30.554998 SMTP response: 220 Welcome
INFO: 2021/11/30 22:07:30.555059 SMTP request: EHLO example.com
INFO: 2021/11/30 22:07:30.555648 SMTP response: 250 Received
INFO: 2021/11/30 22:07:30.555686 SMTP request: QUIT
INFO: 2021/11/30 22:07:30.555722 SMTP response: 221 Closing connection
INFO: 2021/11/30 22:07:30.555732 SMTP session finished
WARNING: 2021/11/30 22:07:30.555801 SMTP mock server is in the shutdown mode and won't accept new connections
INFO: 2021/11/30 22:07:30.555808 SMTP mock server was stopped successfully
Inside of Ruby ecosystem
In Ruby ecosystem smtpmock
is available as smtp_mock
gem. It's flexible Ruby wrapper over smtpmock
binary.
Example of usage
First, you should install smtp_mock
gem and smtpmock
as system dependency:
gem install smtp_mock
bundle exec smtp_mock -i ~
Now, you can create and interact with your smtpmock
instance natively from Ruby ecosystem. It comes with default settings out of the box. Configure only what you need, for example:
require 'smtp_mock'
smtp_mock_server = SmtpMock.start_server(not_registered_emails: %w[user@olo.com user@molo.com])
smtp_mock_server.port
smtp_mock_server.stop!
Inside of any ecosystem
You can use smtpmock
as binary. Just download the pre-compiled binary from the releases page and copy them to the desired location. For start server run command with needed arguments. You can use our bash script for automation this process like in the example below:
curl -sL https://raw.githubusercontent.com/mocktools/go-smtp-mock/master/script/download.sh | bash
./smtpmock -port=2525 -log
Configuring with command line arguments
smtpmock
configuration is available as command line arguments specified in the list below:
Flag description | Example of usage |
---|
-host - host address where smtpmock will run. It's equal to 127.0.0.1 by default | -host=localhost |
-port - server port number. If not specified it will be assigned dynamically | -port=2525 |
-log - enables log server activity. Disabled by default | -log |
-sessionTimeout - session timeout in seconds. It's equal to 30 seconds by default | -sessionTimeout=60 |
-shutdownTimeout - graceful shutdown timeout in seconds. It's equal to 1 second by default | -shutdownTimeout=5 |
-failFast - enables fail fast scenario. Disabled by default | -failFast |
-multipleRcptto - enables multiple RCPT TO receiving scenario. Disabled by default | -multipleRcptto |
-multipleMessageReceiving - enables multiple message receiving scenario. Disabled by default | -multipleMessageReceiving |
-blacklistedHeloDomains - blacklisted HELO domains, separated by commas | -blacklistedHeloDomains="example1.com,example2.com" |
-blacklistedMailfromEmails - blacklisted MAIL FROM emails, separated by commas | -blacklistedMailfromEmails="a@example1.com,b@example2.com" |
-blacklistedRcpttoEmails - blacklisted RCPT TO emails, separated by commas | -blacklistedRcpttoEmails="a@example1.com,b@example2.com" |
-notRegisteredEmails - not registered (non-existent) RCPT TO emails, separated by commas | -notRegisteredEmails="a@example1.com,b@example2.com" |
-responseDelayHelo - HELO response delay in seconds. It's equal to 0 seconds by default | -responseDelayHelo=2 |
-responseDelayMailfrom - MAIL FROM response delay in seconds. It's equal to 0 seconds by default | -responseDelayMailfrom=2 |
-responseDelayRcptto - RCPT TO response delay in seconds. It's equal to 0 seconds by default | -responseDelayRcptto=2 |
-responseDelayData - DATA response delay in seconds. It's equal to 0 seconds by default | -responseDelayData=2 |
-responseDelayMessage - Message response delay in seconds. It's equal to 0 seconds by default | -responseDelayMessage=2 |
-responseDelayRset - RSET response delay in seconds. It's equal to 0 seconds by default | -responseDelayRset=2 |
-responseDelayNoop - NOOP response delay in seconds. It's equal to 0 seconds by default | -responseDelayNoop=2 |
-responseDelayQuit - QUIT response delay in seconds. It's equal to 0 seconds by default | -responseDelayQuit=2 |
-msgSizeLimit - message body size limit in bytes. It's equal to 10485760 bytes | -msgSizeLimit=42 |
-msgGreeting - custom server greeting message | -msgGreeting="Greeting message" |
-msgInvalidCmd - custom invalid command message | -msgInvalidCmd="Invalid command message" |
-msgInvalidCmdHeloSequence - custom invalid command HELO sequence message | -msgInvalidCmdHeloSequence="Invalid command HELO sequence message" |
-msgInvalidCmdHeloArg - custom invalid command HELO argument message | -msgInvalidCmdHeloArg="Invalid command HELO argument message" |
-msgHeloBlacklistedDomain - custom HELO blacklisted domain message | -msgHeloBlacklistedDomain="Blacklisted domain message" |
-msgHeloReceived - custom HELO received message | -msgHeloReceived="HELO received message" |
-msgInvalidCmdMailfromSequence - custom invalid command MAIL FROM sequence message | -msgInvalidCmdMailfromSequence="Invalid command MAIL FROM sequence message" |
-msgInvalidCmdMailfromArg - custom invalid command MAIL FROM argument message | -msgInvalidCmdMailfromArg="Invalid command MAIL FROM argument message" |
-msgMailfromBlacklistedEmail - custom MAIL FROM blacklisted email message | -msgMailfromBlacklistedEmail="Blacklisted email message" |
-msgMailfromReceived - custom MAIL FROM received message | -msgMailfromReceived="MAIL FROM received message" |
-msgInvalidCmdRcpttoSequence - custom invalid command RCPT TO sequence message | -msgInvalidCmdRcpttoSequence="Invalid command RCPT TO sequence message" |
-msgInvalidCmdRcpttoArg - custom invalid command RCPT TO argument message | -msgInvalidCmdRcpttoArg="Invalid command RCPT TO argument message" |
-msgRcpttoNotRegisteredEmail - custom RCPT TO not registered email message | -msgRcpttoNotRegisteredEmail="Not registered email message" |
-msgRcpttoBlacklistedEmail - custom RCPT TO blacklisted email message | -msgRcpttoBlacklistedEmail="Blacklisted email message" |
-msgRcpttoReceived - custom RCPT TO received message | -msgRcpttoReceived="RCPT TO received message" |
-msgInvalidCmdDataSequence - custom invalid command DATA sequence message | -msgInvalidCmdDataSequence="Invalid command DATA sequence message" |
-msgDataReceived - custom DATA received message | -msgDataReceived="DATA received message" |
-msgMsgSizeIsTooBig - custom size is too big message | -msgMsgSizeIsTooBig="Message size is too big" |
-msgMsgReceived - custom received message body message | -msgMsgReceived="Message has been received" |
-msgInvalidCmdRsetSequence - custom invalid command RSET sequence message | -msgInvalidCmdRsetSequence="Invalid command RSET sequence message" |
-msgInvalidCmdRsetArg - custom invalid command RSET message | -msgInvalidCmdRsetArg="Invalid command RSET message" |
-msgRsetReceived - custom RSET received message | -msgRsetReceived="RSET received message" |
-msgNoopReceived - custom NOOP received message | -msgNoopReceived="NOOP received message" |
-msgQuitCmd - custom QUIT command message | -msgQuitCmd="Quit command message" |
Other options
Available not configuration smtpmock
options:
Flag description | Example of usage |
---|
-v - Just prints current smtpmock binary build data (version, commit, datetime). Doesn't run the server. | -v |
Stopping server
smtpmock
accepts 3 shutdown signals: SIGINT
, SIGQUIT
, SIGTERM
.
Implemented SMTP commands
id | Command | Sequenceable | Available args | Example of usage |
---|
1 | HELO | no | domain name , localhost , ip address , [ip address] | HELO example.com |
1 | EHLO | no | domain name , localhost , ip address , [ip address] | EHLO example.com |
2 | MAIL FROM | can be used after command with id 1 and greater | email address , <email address> , localhost email address , <localhost email address> | MAIL FROM: user@domain.com |
3 | RCPT TO | can be used after command with id 2 and greater | email address , <email address> , localhost email address , <localhost email address> | RCPT TO: user@domain.com |
4 | DATA | can be used after command with id 3 | - | DATA |
5 | RSET | can be used after command with id 1 and greater | - | RSET |
6 | NOOP | no | - | NOOP |
7 | QUIT | no | - | QUIT |
Please note in case when same command used more the one time during same session all saved data upper this command will be erased.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/mocktools/go-smtp-mock. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct. Please check the open tickets. Be sure to follow Contributor Code of Conduct below and our Contributing Guidelines.
License
This golang package is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the smtpmock
project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
Credits
Versioning
smtpmock
uses Semantic Versioning 2.0.0