1 module dast.uwsgi; 2 3 import core.thread, 4 dast.map, 5 std.array, 6 std.socket; 7 8 struct Header { 9 align(1): 10 ubyte modifier1; 11 ushort datasize; 12 ubyte modifier2; 13 } 14 15 class Request { 16 enum bufferLen = ushort.max + Header.sizeof; 17 char[bufferLen] buffer = void; 18 Map params; 19 20 protected Socket sock; 21 size_t pos, stop; 22 int requestId; 23 24 @property socket() => sock; 25 26 @property void socket(Socket socket) { 27 pos = 0; 28 stop = 0; 29 params.data.clear(); 30 sock = socket; 31 fillBuffer(); 32 parseParams(); 33 } 34 35 protected bool fillBuffer() { 36 while (pos >= stop) { 37 auto readn = sock.receive(buffer[pos .. $]); 38 if (readn <= 0) { 39 if (sock.isAlive) 40 sock.close(); 41 return false; 42 } 43 stop += readn; 44 } 45 return true; 46 } 47 48 protected void parseParams() { 49 static id = 0; 50 requestId = id++; 51 auto header = cast(Header*)(buffer.ptr + pos); 52 pos += Header.sizeof; 53 fillBuffer(); 54 if (header.modifier1 == 0 && header.modifier2 == 0) { 55 auto p = buffer.ptr + pos; 56 pos += header.datasize - 1; 57 fillBuffer(); 58 for (const end = buffer.ptr + ++pos; p < end;) { 59 auto size = *cast(ushort*)p; 60 p += ushort.sizeof; 61 auto name = p[0 .. size].idup; 62 p += size; 63 size = *cast(ushort*)p; 64 p += ushort.sizeof; 65 params[name] = p[0 .. size].idup; 66 p += size; 67 } 68 } 69 } 70 71 } 72 73 class Response { 74 enum { 75 bufferLen = 1024, 76 maxHeaderLen = 1024 77 } 78 bool headerSent = void; 79 80 protected import tame.buffer : Buffer = FixedBuffer; 81 82 Buffer!maxHeaderLen head; 83 Buffer!bufferLen buf; 84 85 this() { 86 head = typeof(head)(&throwErr!"Headers"); 87 buf = typeof(buf)(&send); 88 } 89 90 int requestId = void; 91 protected Socket sock = void; 92 93 pure @nogc nothrow @safe { 94 void initialize(Socket socket, int reqId) { 95 sock = socket; 96 requestId = reqId; 97 headerSent = false; 98 head.clear(); 99 buf.clear(); 100 } 101 102 void writeHeader(in char[] header) { 103 head ~= header; 104 head ~= "\r\n"; 105 } 106 107 void write(in char[] data) { 108 buf ~= data; 109 } 110 } 111 112 alias put = write; 113 114 void flush() { 115 buf.flush(); 116 } 117 118 void send(in char[] s) { 119 if (!headerSent) { 120 if (!head.length) 121 writeHeader("Content-Type: text/html"); 122 head ~= "\r\n"; 123 sock.send(head.data); 124 head.clear(); 125 headerSent = true; 126 } 127 sock.send(s); 128 } 129 130 void finish() { 131 flush(); 132 sock.close(); 133 } 134 135 protected void throwErr(string obj)(in char[]) { 136 throw new Exception(obj ~ " too long"); 137 } 138 } 139 140 class Server { 141 import std.concurrency; 142 143 alias Handler = void delegate(Request req, Response resp); 144 protected Handler handler; 145 void delegate(Exception e) onError; 146 bool running; 147 Socket listener; 148 Thread[] threads; 149 150 this(Handler dg) { 151 handler = dg; 152 } 153 154 this(void function(Request req, Response resp) fn) { 155 import std.functional; 156 157 handler = toDelegate(fn); 158 } 159 160 void mainLoop() { 161 scope req = new Request; 162 scope resp = new Response; 163 164 while (running) { 165 Socket sock = void; 166 synchronized (listener) 167 try 168 sock = listener.accept(); 169 catch (SocketAcceptException) { 170 running = false; 171 break; 172 } 173 req.socket = sock; 174 resp.initialize(sock, req.requestId); 175 try 176 handler(req, resp); 177 catch (Exception e) { 178 if (onError) 179 onError(e); 180 resp.writeHeader("HTTP/1.1 500 Internal Server Error"); 181 resp.writeHeader("Content-Type: text/html; charset=UTF-8"); 182 resp.write(e.toString()); 183 } 184 resp.finish(); 185 } 186 } 187 188 void start(ushort port = 3031, uint maxThread = 1) { 189 listener = new TcpSocket; 190 listener.bind(new InternetAddress(INADDR_LOOPBACK, port)); 191 listener.listen(128); 192 193 running = true; 194 if (!maxThread) 195 return mainLoop(); 196 197 threads = uninitializedArray!(Thread[])(maxThread); 198 foreach (ref thread; threads) { 199 thread = new Thread(&mainLoop); 200 thread.start(); 201 } 202 } 203 }