eventcore/examples/http-server.d
2017-01-23 11:03:36 +01:00

121 lines
3.4 KiB
D

/++ dub.sdl:
name "http-server-example"
description "Simple pseudo HTTP server suitable for benchmarking"
dependency "eventcore" path=".."
+/
module http_server_example;
import eventcore.core;
import eventcore.internal.utils;
import std.functional : toDelegate;
import std.socket : InternetAddress;
import std.exception : enforce;
import core.time : Duration;
void main()
{
print("Starting up...");
auto addr = new InternetAddress("127.0.0.1", 8080);
auto listener = eventDriver.sockets.listenStream(addr, toDelegate(&onClientConnect));
enforce(listener != StreamListenSocketFD.invalid, "Failed to listen for connections.");
print("Listening for requests on port 8080...");
while (eventDriver.core.waiterCount)
eventDriver.core.processEvents(Duration.max);
}
void onClientConnect(StreamListenSocketFD listener, StreamSocketFD client, scope RefAddress)
@trusted /*@nogc*/ nothrow {
import core.stdc.stdlib;
auto handler = cast(ClientHandler*)calloc(1, ClientHandler.sizeof);
handler.client = client;
handler.handleConnection();
}
struct ClientHandler {
@safe: /*@nogc:*/ nothrow:
alias LineCallback = void delegate(ubyte[]);
StreamSocketFD client;
ubyte[1024] linebuf = void;
size_t linefill = 0;
LineCallback onLine;
@disable this(this);
void handleConnection()
{
//import core.thread;
//() @trusted { print("Connection %d %s", client, cast(void*)Thread.getThis()); } ();
readLine(&onRequestLine);
}
void readLine(LineCallback on_line)
{
onLine = on_line;
if (linefill >= 2) onReadData(client, IOStatus.ok, 0);
else eventDriver.sockets.read(client, linebuf[linefill .. $], IOMode.once, &onReadData);
}
void onRequestLine(ubyte[] ln)
{
//print("Request: %s", cast(char[])ln);
if (ln.length == 0) {
//print("Error: empty request line");
eventDriver.sockets.shutdown(client, true, true);
eventDriver.sockets.releaseRef(client);
}
readLine(&onHeaderLine);
}
void onHeaderLine(ubyte[] ln)
{
if (ln.length == 0) {
auto reply = cast(const(ubyte)[])"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 13\r\nKeep-Alive: timeout=10\r\n\r\nHello, World!";
eventDriver.sockets.write(client, reply, IOMode.all, &onWriteFinished);
} else readLine(&onHeaderLine);
}
void onWriteFinished(StreamSocketFD fd, IOStatus status, size_t len)
{
readLine(&onRequestLine);
}
void onReadData(StreamSocketFD, IOStatus status, size_t bytes_read)
{
import std.algorithm : countUntil;
if (status != IOStatus.ok) {
print("Client disconnect");
eventDriver.sockets.shutdown(client, true, true);
eventDriver.sockets.releaseRef(client);
return;
}
linefill += bytes_read;
assert(linefill <= linebuf.length);
auto idx = linebuf[0 .. linefill].countUntil(cast(const(ubyte)[])"\r\n");
if (idx >= 0) {
assert(linefill + idx <= linebuf.length, "Not enough space to buffer the incoming line.");
linebuf[linefill .. linefill + idx] = linebuf[0 .. idx];
foreach (i; 0 .. linefill - idx - 2)
linebuf[i] = linebuf[idx+2+i];
linefill -= idx + 2;
onLine(linebuf[linefill + idx + 2 .. linefill + idx + 2 + idx]);
} else if (linebuf.length - linefill > 0) {
eventDriver.sockets.read(client, linebuf[linefill .. $], IOMode.once, &onReadData);
} else {
// ERROR: header line too long
print("Header line too long");
eventDriver.sockets.shutdown(client, true, true);
eventDriver.sockets.releaseRef(client);
}
}
}