1 module dast.usockets;
2 
3 import libusockets;
4 import std.typecons;
5 
6 pragma(LDC_no_moduleinfo);
7 pragma(LDC_no_typeinfo):
8 
9 private alias CB = extern (C) void function(us_loop_t* loop);
10 
11 alias integrate = us_loop_integrate;
12 
13 @nogc struct EventLoop(T = void[0]) {
14 	alias LoopCallback = extern (C) void function(typeof(this) loop);
15 
16 	private us_loop_t* loop;
17 	// 需要保证EventLoop和us_loop_t的大小一致
18 	enum uint extSize = T.sizeof;
19 
20 	@property handle() => loop;
21 	alias handle this;
22 
23 	@disable this();
24 
25 	this(us_loop_t* handle) {
26 		loop = handle;
27 	}
28 
29 	this(typeof(null)) {
30 		this(LoopCallback.init);
31 	}
32 
33 	this(LoopCallback onWakeUp,
34 		LoopCallback onPre = null,
35 		LoopCallback onPost = null) {
36 		if (!onWakeUp)
37 			onWakeUp = &noop;
38 		if (!onPre)
39 			onPre = &noop;
40 		if (!onPost)
41 			onPost = &noop;
42 		loop = us_create_loop(null, cast(CB)onWakeUp, cast(CB)onPre, cast(CB)onPost, extSize);
43 	}
44 
45 	void run() => us_loop_run(loop);
46 
47 	void wakeup() => us_wakeup_loop(loop);
48 
49 	@property ref data() @trusted => *cast(T*)us_loop_ext(loop);
50 	@property long iteration() const => us_loop_iteration_number(loop);
51 
52 	void free() => us_loop_free(loop);
53 
54 	auto timer(int fallthrough = 0, int extSize = 0)
55 		=> us_create_timer(loop, fallthrough, extSize);
56 
57 	extern (C) static void noop(typeof(this)) {
58 	}
59 }
60 
61 template eventLoop(T = void[0]) {
62 	alias LoopCallback = extern (C) void function(EventLoop!T loop);
63 
64 	auto eventLoop(LoopCallback onWakeUp = null,
65 		LoopCallback onPre = null,
66 		LoopCallback onPost = null) => EventLoop!T(onWakeUp, onPre, onPost);
67 }
68 
69 struct SSLOptions {
70 	string key;
71 	string cert;
72 	string passphrase;
73 	string dhParams;
74 	string caFile;
75 	string sslCiphers;
76 	bool preferLowMemoryUsage;
77 }
78 
79 /// 将函数参数第一个类型替换为第二个类型参数,返回这个类型
80 private template ReplaceFirstParam(F, T) {
81 	import std.traits;
82 
83 	static if (isFunctionPointer!F) {
84 		alias ReplaceFirstParam = SetFunctionAttributes!(
85 			T function(T, Parameters!F[1 .. $]), functionLinkage!F, functionAttributes!F);
86 	} else static if (isDelegate!F) {
87 		alias ReplaceFirstParam = SetFunctionAttributes!(
88 			T delegate(T, Parameters!F[1 .. $]), functionLinkage!F, functionAttributes!F);
89 	} else
90 		static assert(0, "Unsupported type");
91 }
92 
93 private alias R(T) = ReplaceFirstParam!(T, us_socket_t*);
94 
95 /++
96 Socket context
97 Params:
98 	T: User data type
99 	S: Socket data type
100 	SSL: Whether to use SSL
101 +/
102 @nogc struct Context(CT = void[0], ST = void[0], bool SSL = false) {
103 	private alias S = Socket,
104 	OnSocketOpen = extern (C) S function(S socket, int isClient, char* ip, int ipLength),
105 	OnSocketClose = extern (C) S function(S socket, int code, void* reason),
106 	OnSocketData = extern (C) S function(S socket, char* data, int length),
107 	OnSocketEnd = extern (C) S function(S socket),
108 	OnSocketWritable = OnSocketEnd,
109 	OnSocketTimeout = OnSocketEnd;
110 	private us_socket_context_t* context;
111 	enum int extSize = CT.sizeof;
112 	enum ssl = SSL;
113 
114 	@property handle() => context;
115 	alias handle this;
116 
117 	@property ref data() @trusted => *cast(CT*)us_socket_context_ext(ssl, context);
118 
119 	@property loop() const
120 		=> EventLoop!()(us_socket_context_loop(0, context));
121 
122 	this(us_socket_context_t* handle) {
123 		context = handle;
124 	}
125 
126 	this(us_loop_t* loop, SSLOptions sslOptions = SSLOptions()) {
127 		us_socket_context_options_t options = {
128 			sslOptions.key.ptr,
129 			sslOptions.cert.ptr,
130 			sslOptions.passphrase.ptr,
131 			sslOptions.dhParams.ptr,
132 			sslOptions.caFile.ptr,
133 			sslOptions.sslCiphers.ptr,
134 			sslOptions.preferLowMemoryUsage,
135 		};
136 		context = us_create_socket_context(ssl, loop, extSize, options);
137 	}
138 
139 	void free() => us_socket_context_free(ssl, context);
140 
141 	void onOpen(OnSocketOpen onOpen)
142 		=> us_socket_context_on_open(ssl, context, cast(R!OnSocketOpen)onOpen);
143 
144 	void onData(OnSocketData onData)
145 		=> us_socket_context_on_data(ssl, context, cast(R!OnSocketData)onData);
146 
147 	void onWritable(OnSocketWritable onWritable)
148 		=> us_socket_context_on_writable(ssl, context, cast(R!OnSocketWritable)onWritable);
149 
150 	void onTimeout(OnSocketTimeout onTimeout)
151 		=> us_socket_context_on_timeout(ssl, context, cast(R!OnSocketTimeout)onTimeout);
152 
153 	void onClose(OnSocketClose onClose)
154 		=> us_socket_context_on_close(ssl, context, cast(R!OnSocketClose)onClose);
155 
156 	void onEnd(OnSocketEnd onEnd)
157 		=> us_socket_context_on_end(ssl, context, cast(R!OnSocketEnd)onEnd);
158 
159 	auto listen(string host, ushort port)
160 		=> Listener(
161 			us_socket_context_listen(ssl, context, host.ptr, port, 0, ST.sizeof), ssl);
162 
163 	struct Socket {
164 		private us_socket_t* socket;
165 
166 		@property handle() => socket;
167 		alias handle this;
168 
169 		@property ref data() => *cast(ST*)us_socket_ext(ssl, socket);
170 
171 		@property bool isShutdown() const
172 			=> us_socket_is_shut_down(ssl, socket) != 0;
173 
174 		@property bool isEstablished() const
175 			=> us_socket_is_established(ssl, socket) != 0;
176 
177 		@property context() const
178 			=> Context(us_socket_context(ssl, socket));
179 
180 		@property timeout(uint secs) => us_socket_timeout(ssl, socket, secs);
181 
182 		@disable this();
183 
184 		this(us_socket_t* handle) {
185 			socket = handle;
186 		}
187 
188 		auto close(int code = 0, void* reason = null)
189 			=> Socket(us_socket_close(ssl, socket, code, reason));
190 
191 		void shutdown() => us_socket_shutdown(ssl, socket);
192 
193 		void shutdownRead() => us_socket_shutdown_read(ssl, socket);
194 
195 		void flush() => us_socket_flush(0, socket);
196 
197 		int write(const(void)[] data, bool more = false)
198 			=> us_socket_write(ssl, socket, cast(const char*)data.ptr, cast(int)data.length, more);
199 	}
200 }
201 
202 struct Listener {
203 	private us_listen_socket_t* socket;
204 	bool ssl;
205 
206 	@disable this();
207 
208 	@property handle() => socket;
209 	alias handle this;
210 
211 	this(us_listen_socket_t* handle, bool ssl) {
212 		socket = handle;
213 		this.ssl = ssl;
214 	}
215 
216 	T opCast(T : bool)() const => socket !is null;
217 
218 	void close() => us_listen_socket_close(ssl, socket);
219 }