/serverino

Small and ready-to-go http server, in D

Primary LanguageDMIT LicenseMIT

serverino Logo

BUILD & TEST LICENSE Donate


  • 🚀 Quick build & start: build & run your server in seconds.
  • 🙌 Zero dependencies: serverino doesn’t rely on any external library.
  • 💪 High performance: capable of managing tens of thousands of connections per second.
  • 🌐 Cross-platform: every release is tested on Linux, Windows, and MacOS.

Quickstart

dub init your_app_name -t serverino
cd your_app_name
dub run

A simple webserver in just three lines

import serverino;
mixin ServerinoMain;
void simple(Request request, Output output) { output ~= request.dump(); }

Documentation you need

  • Serverino docs - Serverino reference, generated from code
  • Examples - Some ready-to-try examples
  • Tips - Some snippets you want to read

Defining more than one endpoint

Important

All the functions marked with @endpoint will be called one by one, in order of @priority (higher to lower), until one of them modifies the output.

module app;

import std;
import serverino;

mixin ServerinoMain;

// This endpoint handles the root of the server. Try: http://localhost:8080/
@endpoint @route!"/"
void homepage(Request request, Output output)
{
	output ~= `<html><body>`;
	output ~= `<a href="/private/profile">Private page</a><br>`;
	output ~= `<a href="/private/asdasd">Private (404) page</a>`;
	output ~= `</body></html>`;
}

// This endpoint shows a private page: it's protected by the auth endpoint below.
@endpoint @route!"/private/profile"
void user(Request request, Output output)
{
	output ~= "Hello user!";
}

// This endpoint shows a private page: it's protected by the auth endpoint below.
@endpoint
void blah(Request request, Output output)
{
	// Same as marking this endpoint with @route!"/private/dump"
	if (request.path != "/private/dump")
		return;

	output ~= request.dump();
}

// This endpoint simply checks if the user and password are correct for all the private pages.
// Since it has a higher priority than the previous endpoints, it will be called first.
@priority(10)
@endpoint @route!(r => r.path.startsWith("/private/"))
void auth(Request request, Output output)
{
	if (request.user != "user" || request.password != "password")
	{
		// If the user and password are not correct, we return a 401 status code and a www-authenticate header.
		output.status = 401;
		output.addHeader("www-authenticate",`Basic realm="my serverino"`);
	}

	// If the user and password are correct, we call the next matching endpoint.
	// (the next matching endpoint will be called only if the current endpoint doesn't write anything)
}

// This endpoint has the highest priority between the endpoints and it logs all the requests.
@priority(12) @endpoint
void requestLog(Request request)
{
	// There's no http output, so the next endpoint will be called.
	info("Request: ", request.path);
}

Websockets

Note

Priority works in the same way as the HTTP endpoints: the first websocket endpoint that touches the connection will handle it

// Accept a new connection only if the request path is "/echo"
// Every websocket will start a new indipendent process
@onWebSocketUpgrade bool onUpgrade(Request req) {
	return req.path == "/echo";
}

// Handle the WebSocket connection
// Just like a normal endpoint, but with a WebSocket parameter
@endpoint void echo(Request r, WebSocket ws) {

	// Keep the websocket running
	while (true) {

		// Try to receive a message from the client
		if (WebSocketMessage msg = ws.receiveMessage())
		{
			// If we received a message, send it back to the client
			ws.send("I received your message: `" ~ msg.asString ~ "`");
		}
	}

	// When the function returns, the WebSocket connection is closed
}

Shielding the whole thing

Caution

I recommend securing serverino behind a full web server. Below, I provide two examples of how to run serverino with nginx and apache.

Using nginx

It's pretty easy. Just add these lines inside your nginx configuration:

server {
   listen 80 default_server;
   listen [::]:80 default_server;

   location /your_path/ {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;

      proxy_pass http://localhost:8080;
   }
   ...
   ...
}

If you want to enable keepalive (between nginx and serverino) you must use an upstream:

upstream your_upstream_name {
  server localhost:8080;
  keepalive 64;
}


server {
   listen 80 default_server;
   listen [::]:80 default_server;

   location /your_path/ {
      proxy_set_header Connection "";
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;

      proxy_pass http://your_upstream_name;
    }

    ...
    ...
 }

Using apache2

Enable proxy module for apache2:

sudo a2enmod proxy
sudo a2enmod proxy_http

Add a proxy in your virtualhost configuration:

<VirtualHost *:80>
   ProxyPass "/"  "http://localhost:8080/"
   ...
</VirtualHost>