1 module lighttp.server;
2 
3 import std.system : Endian;
4 
5 import libasync;
6 
7 import xbuffer;
8 
9 import lighttp.router;
10 import lighttp.util;
11 
12 private enum __lighttp = "lighttp/0.1.0";
13 
14 class Server {
15 
16 	private EventLoop evl;
17 	private Router router;
18 
19 	private immutable string name;
20 
21 	this(R:Router)(EventLoop evl, R router, string name=__lighttp) {
22 		this.evl = evl;
23 		registerRoutes(router);
24 		this.router = router;
25 		this.name = name;
26 	}
27 
28 	this(R:Router)(R router, string name=__lighttp) {
29 		this(getThreadEventLoop(), router, name);
30 	}
31 
32 	@property EventLoop eventLoop() pure nothrow @safe @nogc {
33 		return this.evl;
34 	}
35 
36 	void host(string ip, ushort port) {
37 		auto listener = new AsyncTCPListener(this.evl);
38 		listener.host(ip, port);
39 		listener.run(&this.handler);
40 	}
41 
42 	void delegate(TCPEvent) handler(AsyncTCPConnection conn) {
43 		auto ret = new Connection(conn);
44 		return &ret.handle;
45 	}
46 
47 	class Connection {
48 
49 		AsyncTCPConnection conn;
50 
51 		private void delegate(TCPEvent) _handler;
52 
53 		this(AsyncTCPConnection conn) {
54 			this.conn = conn;
55 			_handler = &handleHTTP;
56 		}
57 
58 		void handle(TCPEvent event) {
59 			_handler(event);
60 		}
61 
62 		void handleHTTP(TCPEvent event) {
63 			if(event == TCPEvent.READ) {
64 				auto req = new Typed!(immutable char)(16);
65 				static ubyte[] buffer = new ubyte[1024];
66 				while(true) {
67 					auto len = this.conn.recv(buffer);
68 					if(len > 0) req.write(buffer[0..len]);
69 					if(len < buffer.length) break;
70 				}
71 				import std.stdio : writeln;
72 				writeln(req.data);
73 				Request request = new Request();
74 				Response response = new Response();
75 				response.headers["Server"] = name;
76 				HandleResult result;
77 				if(request.parse(req.data)) {
78 					try router.handle(result, this.conn, request, response);
79 					catch(Exception) response.status = StatusCodes.internalServerError;
80 				} else {
81 					response.status = StatusCodes.badRequest;
82 				}
83 				if(response.status.code >= 400) router.error(request, response);
84 				this.conn.send(cast(ubyte[])response.toString());
85 				if(result.webSocket is null) {
86 					this.conn.kill();
87 				} else {
88 					_handler = &result.webSocket.handle;
89 					result.callOnConnect();
90 				}
91 			}
92 		}
93 
94 	}
95 
96 }
97 
98 /**
99  * Base class for web socket clients.
100  */
101 class WebSocketClient {
102 
103 	AsyncTCPConnection conn; // set by the router
104 
105 	private Typed!ubyte buffer;
106 
107 	this() {
108 		this.buffer = new Typed!ubyte(1024);
109 	}
110 
111 	final void handle(TCPEvent event) {
112 		switch(event) with(TCPEvent) {
113 			case READ:
114 				this.buffer.reset();
115 				static ubyte[] buffer = new ubyte[1024];
116 				while(true) {
117 					auto len = this.conn.recv(buffer);
118 					if(len > 0) this.buffer.write(buffer[0..len]);
119 					if(len < buffer.length) break;
120 				}
121 				try if((this.buffer.get() & 0b1111) == 1) {
122 					immutable info = this.buffer.get();
123 					immutable masked = (info & 0b10000000) != 0;
124 					size_t length = info & 0b01111111;
125 					if(length == 0b01111110) {
126 						length = this.buffer.read!(Endian.bigEndian, ushort)();
127 					} else if(length == 0b01111111) {
128 						length = this.buffer.read!(Endian.bigEndian, ulong)() & size_t.max;
129 					}
130 					if(masked) {
131 						ubyte[] mask = this.buffer.get(4);
132 						ubyte[] data = this.buffer.get(length);
133 						foreach(i, ref ubyte p; data) {
134 							p ^= mask[i % 4];
135 						}
136 						this.onReceive(data);
137 					} else {
138 						this.onReceive(this.buffer.get(length));
139 					}
140 				} catch(BufferOverflowException) {}
141 				break;
142 			case CLOSE:
143 				this.onClose();
144 				break;
145 			default:
146 				break;
147 		}
148 	}
149 	
150 	/**
151 	 * Sends data to the connected web socket.
152 	 */
153 	void send(in void[] data) {
154 		this.buffer.reset();
155 		this.buffer.put(0b10000001);
156 		if(data.length < 0b01111110) {
157 			this.buffer.put(data.length & ubyte.max);
158 		} else if(data.length < ushort.max) {
159 			this.buffer.put(0b01111110);
160 			this.buffer.write!(Endian.bigEndian, ushort)(data.length & ushort.max);
161 		} else {
162 			this.buffer.put(0b01111111);
163 			this.buffer.write!(Endian.bigEndian, ulong)(data.length);
164 		}
165 		this.buffer.write(data);
166 		this.conn.send(this.buffer.data);
167 	}
168 
169 	/**
170 	 * Notifies that the client has sent some data.
171 	 */
172 	abstract void onReceive(ubyte[] data);
173 
174 	/**
175 	 * Notifies that the connection has been interrupted.
176 	 */
177 	abstract void onClose();
178 
179 }