1 module dast.http.server;
2 
3 import dast.async;
4 
5 public import dast.async : EventLoop, EventExecutor;
6 public import dast.http : Request, Status, ServerSettings;
7 
8 alias NextHandler = void delegate(),
9 ReqHandler = void function(HTTPServer server, HTTPClient client, scope NextHandler next);
10 
11 ///
12 @safe class HTTPClient : TCPClient {
13 	Request request;
14 	bool keepConnection;
15 
16 nothrow:
17 	@property id() const => cast(int)handle;
18 	/// Whether the header has been sent
19 	@property headerSent() const => _headerSent;
20 	private bool tryParse() => request.tryParse(data);
21 
22 	this(EventLoop loop, Socket socket, uint bufferSize = 4 * 1024) @trusted {
23 		super(loop, socket, bufferSize);
24 		p = _rBuf.ptr;
25 	}
26 
27 	void writeHeader(in char[] data) pure nothrow @safe {
28 		header ~= data;
29 		header ~= "\r\n";
30 	}
31 
32 	void send(in char[] msg) @trusted {
33 		if (!_headerSent) {
34 			if (!header.length)
35 				writeHeader("Content-Type: text/html");
36 			header ~= "Transfer-Encoding: chunked\r\n\r\n";
37 			write(header);
38 			header.length = 0;
39 			_headerSent = true;
40 		}
41 		char[18] b = void;
42 		auto p = intToHex(b.ptr, msg.length);
43 		*p = '\r';
44 		*(p + 1) = '\n';
45 		write(b[0 .. p - b.ptr + 2].dup);
46 		write(msg);
47 		write("\r\n");
48 	}
49 
50 	private void clear() {
51 		clearQueue();
52 		header.length = 0;
53 		_headerSent = false;
54 	}
55 
56 	void finish() {
57 		write("0\r\n\r\n");
58 		flush();
59 		reset();
60 		if (!keepConnection)
61 			close();
62 	}
63 
64 	void reset() @trusted {
65 		_headerSent = false;
66 		_rBuf = p[0 .. _rBuf.ptr - p + _rBuf.length];
67 	}
68 
69 protected:
70 	@property data() const @trusted => p[0 .. _rBuf.ptr - p];
71 
72 	bool put(size_t len) @trusted {
73 		if (_rBuf.ptr - p + len >= bufferSize)
74 			return false;
75 		_rBuf = _rBuf[len .. $];
76 		return true;
77 	}
78 
79 	typeof(_rBuf.ptr) p;
80 	char[] header;
81 	bool _headerSent;
82 }
83 
84 class HTTPServer : TCPServer {
85 	private ReqHandler[] handlers;
86 	ServerSettings settings;
87 	uint connections;
88 
89 	this(AddrFamily family = AddrFamily.IPv4) {
90 		super(new EventLoop, family);
91 	}
92 
93 	this(EventLoop loop, AddrFamily family = AddrFamily.IPv4) {
94 		super(loop, family);
95 	}
96 
97 	void run() {
98 		bindAndListen(_socket, settings);
99 		if (!onAccept)
100 			onAccept = (Socket socket) {
101 			if (settings.maxConnections && connections >= settings.maxConnections) {
102 				socket.close();
103 				return;
104 			}
105 			auto client = new HTTPClient(_loop, socket, settings.bufferSize);
106 			connections++;
107 			client.onReceived = (in ubyte[] data) @trusted {
108 				if (!client.put(data.length))
109 					client.close();
110 				if (!client.tryParse())
111 					return;
112 				client.keepConnection = client.request.headers.connection != "close";
113 				try {
114 					size_t i;
115 					scope NextHandler next;
116 					next = () {
117 						if (i < handlers.length)
118 							handlers[i++](this, client, next);
119 					};
120 					next();
121 				} catch (Exception e) {
122 					debug(Log) error(e);
123 					client.clear();
124 					client.writeHeader("HTTP/1.1 " ~ Status.Error);
125 					client.writeHeader("Content-Type: text/html; charset=UTF-8");
126 					client.send(e.toString());
127 				}
128 				client.finish();
129 			};
130 			client.onClosed = () { connections--; };
131 			client.start();
132 		};
133 		start();
134 		(cast(EventLoop)_loop).run();
135 	}
136 
137 nothrow:
138 	void use(ReqHandler handler)
139 	in (handler) {
140 		handlers ~= handler;
141 	}
142 }
143 
144 package(dast) void bindAndListen(Socket socket, in ServerSettings settings) @safe {
145 	import tame.string;
146 
147 	socket.reusePort = settings.reusePort;
148 	foreach (host; splitter(settings.listen, ';')) {
149 		host = host.stripLeft();
150 		const(char)[] port;
151 		const i = host.indexOf(':');
152 		if (~i) {
153 			port = host[i + 1 .. $];
154 			host = host[0 .. i];
155 		}
156 		foreach (addr; getAddress(host.length ? host : "localhost", port)) {
157 			if (addr.addressFamily == socket.addressFamily)
158 				socket.bind(addr);
159 		}
160 	}
161 	socket.listen(settings.connectionQueueSize);
162 }
163 
164 private auto intToHex(char* buf, size_t value) {
165 	char* p = buf;
166 	for (;;) {
167 		const n = cast(int)value & 0xf ^ '0';
168 		*p++ = cast(char)(n < 58 ? n : n + 39);
169 		if (value < 16)
170 			break;
171 		value >>= 4;
172 	}
173 	for (char* i = buf, j = p - 1; i < j; i++, j--) {
174 		char t = *i;
175 		*i = *j;
176 		*j = t;
177 	}
178 	return p;
179 }
180 
181 unittest {
182 	char[16] buf = void;
183 	auto str = buf[];
184 	auto p = buf.ptr;
185 	assert(intToHex(p, 0) - p == 1);
186 	assert(str[0 .. 1] == "0", str);
187 	assert(intToHex(p, 0xff) - p == 2);
188 	assert(str[0 .. 2] == "ff", str);
189 	assert(intToHex(p, 0x12345678) - p == 8, str);
190 	assert(str[0 .. 8] == "12345678", str);
191 	assert(intToHex(p, size_t.max) - p == 16, str);
192 	assert(str == "ffffffffffffffff", str);
193 }