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 }