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 }