Implementing an event-driven HTTP server with Scala Native

Implementing an event-driven HTTP server with Scala Native

Scala Native allows you to compile your Scala code to a native executable using LLVM. This is good for two reasons:

Because your code is compiled instead of running on the JVM, it can run more quickly, depending on your use case. In particular it means your application will not suffer the JVM warm-up overhead when it starts up, making Scala Native suitable for command-line tools.

It allows your Scala code to interoperate with C/C++ libraries and other native code. For example you can use the C stdlib to malloc and free memory.

In this post I'm going to introduce Scala Native by using it to write a simple HTTP server using a C library called libuv. Along the way we'll look at:

how to work with Scala libraries that have been cross-built for Scala Native

Quite a lot of stuff just happened! The Scala Native compiler plugin and sbt-based tooling took the Scala code, transpiled it into a Scala Native-specific representation called NIR (Native Intermediate Representation), then from NIR into LLVM IR. Then LLVM compiled it into assembly language, i.e. native code, and linked it into an executable binary. Finally the binary was executed and it printed a friendly message to the screen.

This diagram shows the whole pipeline. The blue boxes are tools provided by Scala Native, while the orange ones are part of the LLVM toolchain.

You can run the generated binary directly from the command line if you want to:

$ target/scala-2.11/scala-native-webserver-out
Hello, World!

IntelliJ gotcha

If you open the project in IntelliJ, you'll find that a bunch of stuff from the Scala standard library (e.g. println) shows as red. This is a known issue.

One workaround is to open up "Project Structure" and manually remove the scalalib library (the one highlighted in the screenshot below).

Be aware that you will need to do this every time you re-import the project from sbt.

Adding a Scala dependency

Now that we've conquered "hello world", let's get started on building a webserver.

Define data types

This is a 2-field struct representing a buffer. It has a pointer to the actual bytes of data, and a field containing the number of bytes.

This data type was nice and simple, but for more complex structs with lots of fields and nested structs, defining the corresponding Scala type by hand is pretty tiresome.

It's also quite tricky to define the type correctly, so that each field in your Scala struct type is exactly the right size and thus matches the corresponding field in the C struct. I ended up with crashes at runtime because my struct types were not as big as they should have been, meaning I was not allocating as many bytes as libuv expected. The library was writing data to bytes that had not actually been allocated.

Define functions

Defining facades for external functions is a lot simpler. You just define the function signature, which must match the signature that the library exposes, and set the right-hand side of the function to extern:

The first thing to notice is that the whole function body is wrapped in a Zone block. This is so that we can use Scala Native's handy zone allocation feature. Any memory allocated using alloc inside a Zone block is automatically released at the end of the block, so you don't need to worry about freeing it manually.

Unfortunately, due to the callback-driven nature of the libuv API, we won't be able to use zone allocation in the rest of the program. Memory often needs to be allocated in one place and later freed in a callback somewhere else in the program. So we will have to use good old malloc and free, and try really hard not to leak any memory!

Hopefully the code above is reasonably self-explanatory. bailOnError is a tiny helper function I wrote that prints some useful error information and quits the program if the given call to a libuv function returns a negative status code.

Server.onTcpConnection is a callback that is called when a client connects to the server. We haven't implemented it yet. Let's do that now.

The second line of the _onTcpConnection function deserves some explanation:

val loop: Ptr[Loop] = (!tcpHandle._2).cast[Ptr[Loop]]

tcpHandle is a pointer to a struct of type TcpHandle (or uv_tcp_t in the C API). !tcpHandle._2 deferences the pointer and reads the second field of the struct, which is a pointer to the event loop. Unfortunately due to a known issue in Scala Native (which I don't fully understand), type information about the field is lost, so you need to cast it to convince the compiler of its type.

Also note that we malloc a handle for the client, but we don't free it yet. We'll need to remember to free this later when the connection is closed.

You should now be able to run the server and connect to it using curl localhost:7000. When you connect, you'll see the server print the appropriate log messages to stdout. But because the server doesn't respond with anything yet, curl will just hang.

Handling the request data

Next we'll implement the onRead callback to handle incoming data from the client.

This callback will be called one or more times per request. If it's a small request message (say an HTTP GET), it will only be called once, but if the request is large then it will be read in chunks and the callback will be called once per chunk.

For simplicity we'll assume we are only handling requests that can be read in a single chunk. So as soon as we have read the first chunk of data, we'll turn it into a String, parse it as an HTTP request and write an appropriate response.

Now we have a parser, we can fill in the parseAndRespond function. Here is where a real server would do something useful, e.g. serve a file from the filesystem. But for the sake of demonstration we'll just parrot back some information that we've parsed from the request.

All together now

No doubt you're pretty lost after reading through all these callback-riddled code snippets. It might make more sense if you look at the complete working code, which is available on GitHub.

It works!

If you run the app and access localhost:7000 in your browser, you should see something like this.

Summary

If you value the safety of strong static types but you also enjoy shooting yourself in the foot by messing up your pointer arithmetic, then Scala Native is the project for you! Just kidding. This post was a lot of fun to write, and I can really see the potential of Scala Native.

The user experience is a little rough around the edges at the moment (e.g. I encountered a lot of incomprehensible stack traces from the compiler plugin and had to solve them pretty much through trial and error). But the fact that it works at all is pretty miraculous, and is testament to the amount of hard work put into the project by Denys Shabalin and the rest of the Scala Native contributors. I'm sure as time goes by and the project matures, it will grow into a pillar of the Scala ecosystem.