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 }