Writing a SMTP server with Akka
Today I want to show you how to implement a purely reactive Simple Mail Transfer Protocol server with Akka. Of course at heart there will be actors involved and so, if you are not familiar with Akka and actors I suggest to go to Akka’s getting started guide first. The implementation we will dig in today is far away from beeing production ready. In particular things like Authentication, SSL/STARTTLS, Spamchecking and so on are out of scope of this article.
As I assumed that you have basic knowledge on Akka I will just explain the basic skeleton with a few words:
We have an object
Application that acts as entry point and just creates one instance of the class
Application and then executes the
run() method. The
Bootable trait defines two methods
shutdown whereas the
shutdown method is registered to be invoked before the application is shut down (for example due to a
SIG_INT). The Actor system is created on instantiation and is shutdown before termination. Note, that we wait at most 3 seconds for the actor system to acutally shutdown. If it takes longer the JVM exit will force the actor system to death.
Applicationclass/object can be reused in all server like Akka applications.
The next obvious thing we need is something to listen for incoming TCP connections. This is what the
TcpServer actor is made for:
TcpServer takes a local address to bind to, for example
new InetSocketAddress("0.0.0.0", 25) to listen on port 25 on all network interfaces. In addition it takes a function of type
ActorRef ⇒ Props that allows the creating of new handler actors (see step 3 in the following illustration). The creation function is given the TCP actor from
akka.io that we have to register our handler actor with and must return an actor (or more precisely the
Props for an actor).
TcpServerclass can be reused for any TCP server.
The most basic “Hello World” style TCP handler actor might just respond to every incoming byte string with exactly the same byte string. This, let’s call it
EchoTcpHandler, could look like this:
Suppose we wanted to build such a service, then in our
Application.startup method we would just add:
val bind = new InetSocketAddress("0.0.0.0", 12345)
What does this do? The first line just defined on what port and what interface to listen on. The second line creates a new
TcpServer actor and tells him, that on every incoming connection there should be a new
EchoTcpHandler created to actually handle the new connection.
Now we already have a fully reactive Akka actor based TCP server that does one thing and that well: Yelling back at you what you yell to him.
SmtpServer is basically a finite state machine reacting to incoming
Tcp.Received events. Take a look at a typical SMTP transmission protocol:
S: 220 foo.com Simple Mail Transfer Service Ready
When a client connects, the server starts with sending a
220 reply to the client and then just waits. Eventually the client responds with an
HELO command and in respond to that the server replies with an
250 reply. This play goes back and forth. In every state there are only some valid commands that a client may send and all others are replied to with an
500 error reply. But if the client sends a valid command then the state of the server may change so that maybe other commands may be valid. Be happy, Akka has very good support for writing reactive finite state machines (see FSM).
We need six distinct states that our (simple) SMTP server can be in. In addition we have two different data types that can be attached to the states:
After registering to the Tcp actor the SmtpServer sends itself a
Register message and then starts up in
State1waiting for the
Register messages. This is kind of a starting shot. When the server receives the message it sends an
220 localhost reply to the client and transists into state
State2. From there on the server waits for either the
EHLO command from the client. When he gets that, he replies with a
250 OK and transists into state
State3. You get the idea, right? The rest is just sticking to the RFC and reacting the the right messages in the right state extracting the necessary information and transisting into the right next state. If the client sends the
QUIT command the server replies with
221 OK, notifies the TCP actor to close the connection and transists into
State0. In this state every incoming message is ignored silently, since the server just waits for the TCP connection to actually end. If this happends, the server actor releases itself.
You might wonder, where the
Reply class come from: These are simple objects with an
unapply(raw: ByteString) method that allow to deconstruct a raw
ByteString into a “parsed” SMTP command/reply. Take a look at SmtpProtocol.scala for more details. It’s really just some string splitting.
We have seen the most important stuff around building an SMTP server with Akka IO. My project at GitHub contains some more advanced stuff like for example:
DelimitedTcpPipelineclass that ensure, that the communication is always line based (i.e. the
SmtpServernever receives a message like
\r\nWorld\r\n, but always well defined complete lines like
LoggingTcpPipelinethat logs all incoming and outgoing messages to the console.
To try it out just execute the following commands from your terminal:
$ git clone https://github.com/choffmeister/akka-smtpserver.git
Now you can open up another terminal and connect via
telnet to port
$ telnet localhost 25252