1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.socket;
7 
8 
9 version(WINE)
10 {
11 	version = DFL_NoSocket;
12 }
13 
14 
15 version(DFL_NoSocket)
16 {
17 }
18 else
19 {
20 
21 private import dfl.internal.dlib, dfl.internal.clib;
22 
23 private
24 {
25 	private import std.socket, core.bitop;
26 	private import std.c.windows.winsock;
27 	
28 	alias InternetHost DInternetHost;
29 	alias InternetAddress DInternetAddress;
30 	
31 	socket_t getSocketHandle(Socket sock)
32 	{
33 		return sock.handle;
34 	}
35 }
36 
37 alias std.socket.Socket DflSocket; ///
38 
39 private import dfl.internal.winapi, dfl.application, dfl.base, dfl.internal.utf;
40 
41 
42 private
43 {
44 	enum
45 	{
46 		FD_READ =       0x01,
47 		FD_WRITE =      0x02,
48 		FD_OOB =        0x04,
49 		FD_ACCEPT =     0x08,
50 		FD_CONNECT =    0x10,
51 		FD_CLOSE =      0x20,
52 		FD_QOS =        0x40,
53 		FD_GROUP_QOS =  0x80,
54 	}
55 	
56 	
57 	extern(Windows) int WSAAsyncSelect(socket_t s, HWND hWnd, UINT wMsg, int lEvent);
58 }
59 
60 
61 ///
62 // Can be OR'ed.
63 enum EventType
64 {
65 	NONE = 0, ///
66 	
67 	READ =       FD_READ, /// ditto
68 	WRITE =      FD_WRITE, /// ditto
69 	OOB =        FD_OOB, /// ditto
70 	ACCEPT =     FD_ACCEPT, /// ditto
71 	CONNECT =    FD_CONNECT, /// ditto
72 	CLOSE =      FD_CLOSE, /// ditto
73 	
74 	QOS =        FD_QOS,
75 	GROUP_QOS =  FD_GROUP_QOS,
76 }
77 
78 
79 ///
80 // -err- will be 0 if no error.
81 // -type- will always contain only one flag.
82 alias void delegate(DflSocket sock, EventType type, int err) RegisterEventCallback;
83 
84 
85 // Calling this twice on the same socket cancels out previously
86 // registered events for the socket.
87 // Requires Application.run() or Application.doEvents() loop.
88 void registerEvent(DflSocket sock, EventType events, RegisterEventCallback callback) // deprecated
89 {
90 	assert(sock !is null, "registerEvent: socket cannot be null");
91 	assert(callback !is null, "registerEvent: callback cannot be null");
92 	
93 	if(!hwNet)
94 		_init();
95 	
96 	sock.blocking = false; // So the getter will be correct.
97 	
98 	// SOCKET_ERROR
99 	if(-1 == WSAAsyncSelect(getSocketHandle(sock), hwNet, WM_DFL_NETEVENT, cast(int)events))
100 		throw new DflException("Unable to register socket events");
101 	
102 	EventInfo ei;
103 	
104 	ei.sock = sock;
105 	ei.callback = callback;
106 	allEvents[getSocketHandle(sock)] = ei;
107 }
108 
109 
110 void unregisterEvent(DflSocket sock) // deprecated
111 {
112 	WSAAsyncSelect(getSocketHandle(sock), hwNet, 0, 0);
113 	
114 	//delete allEvents[getSocketHandle(sock)];
115 	allEvents.remove(getSocketHandle(sock));
116 }
117 
118 
119 ///
120 class AsyncSocket: DflSocket // docmain
121 {
122 	///
123 	this(AddressFamily af, SocketType type, ProtocolType protocol)
124 	{
125 		super(af, type, protocol);
126 		super.blocking = false;
127 	}
128 	
129 	/// ditto
130 	this(AddressFamily af, SocketType type)
131 	{
132 		super(af, type);
133 		super.blocking = false;
134 	}
135 	
136 	/// ditto
137 	this(AddressFamily af, SocketType type, Dstring protocolName)
138 	{
139 		super(af, type, protocolName);
140 		super.blocking = false;
141 	}
142 	
143 	/// ditto
144 	// For use with accept().
145 	protected this()
146 	{
147 	}
148 	
149 	
150 	///
151 	void event(EventType events, RegisterEventCallback callback)
152 	{
153 		registerEvent(this, events, callback);
154 	}
155 	
156 	
157 	protected override AsyncSocket accepting()
158 	{
159 		return new AsyncSocket;
160 	}
161 	
162 	
163 	override void close()
164 	{
165 		unregisterEvent(this);
166 		super.close();
167 	}
168 	
169 	
170 	override @property bool blocking() const // getter
171 	{
172 		return false;
173 	}
174 	
175 	
176 	override @property void blocking(bool byes) // setter
177 	{
178 		if(byes)
179 			assert(0);
180 	}
181 	
182 }
183 
184 
185 ///
186 class AsyncTcpSocket: AsyncSocket // docmain
187 {
188 	///
189 	this(AddressFamily family)
190 	{
191 		super(family, SocketType.STREAM, ProtocolType.TCP);
192 	}
193 	
194 	/// ditto
195 	this()
196 	{
197 		this(cast(AddressFamily)AddressFamily.INET);
198 	}
199 	
200 	/// ditto
201 	// Shortcut.
202 	this(Address connectTo, EventType events, RegisterEventCallback eventCallback)
203 	{
204 		this(connectTo.addressFamily());
205 		event(events, eventCallback);
206 		connect(connectTo);
207 	}
208 }
209 
210 
211 ///
212 class AsyncUdpSocket: AsyncSocket // docmain
213 {
214 	///
215 	this(AddressFamily family)
216 	{
217 		super(family, SocketType.DGRAM, ProtocolType.UDP);
218 	}
219 	
220 	/// ditto
221 	this()
222 	{
223 		this(cast(AddressFamily)AddressFamily.INET);
224 	}
225 }
226 
227 
228 /+
229 private class GetHostWaitHandle: WaitHandle
230 {
231 	this(HANDLE h)
232 	{
233 		super.handle = h;
234 	}
235 	
236 	
237 	final:
238 	
239 	alias WaitHandle.handle handle; // Overload.
240 	
241 	override @property void handle(HANDLE h) // setter
242 	{
243 		assert(0);
244 	}
245 	
246 	override void close()
247 	{
248 		WSACancelAsyncRequest(handle);
249 		super.handle = INVALID_HANDLE;
250 	}
251 	
252 	
253 	private void _gotEvent()
254 	{
255 		super.handle = INVALID_HANDLE;
256 	}
257 }
258 
259 
260 private class GetHostAsyncResult, IAsyncResult
261 {
262 	this(HANDLE h, GetHostCallback callback)
263 	{
264 		wh = new GetHostWaitHandle(h);
265 		this.callback = callback;
266 	}
267 	
268 	
269 	@property WaitHandle asyncWaitHandle() // getter
270 	{
271 		return wh;
272 	}
273 	
274 	
275 	@property bool completedSynchronously() // getter
276 	{
277 		return false;
278 	}
279 	
280 	
281 	@property bool isCompleted() // getter
282 	{
283 		return wh.handle != WaitHandle.INVALID_HANDLE;
284 	}
285 	
286 	
287 	private:
288 	GetHostWaitHandle wh;
289 	GetHostCallback callback;
290 	
291 	
292 	void _gotEvent(LPARAM lparam)
293 	{
294 		wh._gotEvent();
295 		
296 		callback(bla, HIWORD(lparam));
297 	}
298 }
299 +/
300 
301 
302 private void _getHostErr()
303 {
304 	throw new DflException("Get host failure"); // Needs a better message.. ?
305 }
306 
307 
308 private class _InternetHost: DInternetHost
309 {
310 	private:
311 	this(void* hostentBytes)
312 	{
313 		super.validHostent(cast(hostent*)hostentBytes);
314 		super.populate(cast(hostent*)hostentBytes);
315 	}
316 }
317 
318 
319 ///
320 // If -err- is nonzero, it is a winsock error code and -inetHost- is null.
321 alias void delegate(DInternetHost inetHost, int err) GetHostCallback;
322 
323 
324 ///
325 class GetHost // docmain
326 {
327 	///
328 	void cancel()
329 	{
330 		WSACancelAsyncRequest(h);
331 		h = null;
332 	}
333 	
334 	
335 	private:
336 	HANDLE h;
337 	GetHostCallback callback;
338 	DThrowable exception;
339 	ubyte[/+MAXGETHOSTSTRUCT+/ 1024] hostentBytes;
340 	
341 	
342 	void _gotEvent(LPARAM lparam)
343 	{
344 		h = null;
345 		
346 		int err;
347 		err = HIWORD(lparam);
348 		if(err)
349 			callback(null, err);
350 		else
351 			callback(new _InternetHost(hostentBytes.ptr), 0);
352 	}
353 	
354 	
355 	this()
356 	{
357 	}
358 }
359 
360 
361 ///
362 GetHost asyncGetHostByName(Dstring name, GetHostCallback callback) // docmain
363 {
364 	if(!hwNet)
365 		_init();
366 	
367 	HANDLE h;
368 	GetHost result;
369 	
370 	result = new GetHost;
371 	h = WSAAsyncGetHostByName(hwNet, WM_DFL_HOSTEVENT, unsafeStringz(name),
372 		cast(char*)result.hostentBytes, result.hostentBytes.length);
373 	if(!h)
374 		_getHostErr();
375 	
376 	result.h = h;
377 	result.callback = callback;
378 	allGetHosts[h] = result;
379 	
380 	return result;
381 }
382 
383 
384 ///
385 GetHost asyncGetHostByAddr(uint32_t addr, GetHostCallback callback) // docmain
386 {
387 	if(!hwNet)
388 		_init();
389 	
390 	HANDLE h;
391 	GetHost result;
392 	
393 	result = new GetHost;
394 	version(LittleEndian)
395 		addr = bswap(addr);
396 	h = WSAAsyncGetHostByAddr(hwNet, WM_DFL_HOSTEVENT, cast(char*)&addr, addr.sizeof,
397 		AddressFamily.INET, cast(char*)result.hostentBytes, result.hostentBytes.length);
398 	if(!h)
399 		_getHostErr();
400 	
401 	result.h = h;
402 	result.callback = callback;
403 	allGetHosts[h] = result;
404 	
405 	return result;
406 }
407 
408 /// ditto
409 // Shortcut.
410 GetHost asyncGetHostByAddr(Dstring addr, GetHostCallback callback) // docmain
411 {
412 	uint uiaddr;
413 	uiaddr = DInternetAddress.parse(addr);
414 	if(DInternetAddress.ADDR_NONE == uiaddr)
415 		_getHostErr();
416 	return asyncGetHostByAddr(uiaddr, callback);
417 }
418 
419 
420 ///
421 class SocketQueue // docmain
422 {
423 	///
424 	this(DflSocket sock)
425 	in
426 	{
427 		assert(sock !is null);
428 	}
429 	body
430 	{
431 		this.sock = sock;
432 	}
433 	
434 	
435 	///
436 	final @property DflSocket socket() // getter
437 	{
438 		return sock;
439 	}
440 	
441 	
442 	///
443 	void reset()
444 	{
445 		writebuf = null;
446 		readbuf = null;
447 	}
448 	
449 	
450 	/+
451 	// DMD 0.92 says error: function toString overrides but is not covariant with toString
452 	override Dstring toString()
453 	{
454 		return cast(Dstring)peek();
455 	}
456 	+/
457 	
458 	
459 	///
460 	void[] peek()
461 	{
462 		return readbuf[0 .. rpos];
463 	}
464 	
465 	/// ditto
466 	void[] peek(uint len)
467 	{
468 		if(len >= rpos)
469 			return peek();
470 		
471 		return readbuf[0 .. len];
472 	}
473 	
474 	
475 	///
476 	void[] receive()
477 	{
478 		ubyte[] result;
479 		
480 		result = readbuf[0 .. rpos];
481 		readbuf = null;
482 		rpos = 0;
483 		
484 		return result;
485 	}
486 	
487 	/// ditto
488 	void[] receive(uint len)
489 	{
490 		if(len >= rpos)
491 			return receive();
492 		
493 		ubyte[] result;
494 		
495 		result = readbuf[0 .. len];
496 		readbuf = readbuf[len .. readbuf.length];
497 		rpos -= len;
498 		
499 		return result;
500 	}
501 	
502 	
503 	///
504 	void send(void[] buf)
505 	{
506 		if(canwrite)
507 		{
508 			assert(!writebuf.length);
509 			
510 			int st;
511 			if(buf.length > 4096)
512 				st = 4096;
513 			else
514 				st = buf.length;
515 			
516 			st = sock.send(buf[0 .. st]);
517 			if(st > 0)
518 			{
519 				if(buf.length - st)
520 				{
521 					// dup so it can be appended to.
522 					writebuf = (cast(ubyte[])buf)[st .. buf.length].dup;
523 				}
524 			}
525 			else
526 			{
527 				// dup so it can be appended to.
528 				writebuf = (cast(ubyte[])buf).dup;
529 			}
530 			
531 			//canwrite = false;
532 		}
533 		else
534 		{
535 			writebuf ~= cast(ubyte[])buf;
536 		}
537 	}
538 	
539 	
540 	///
541 	// Number of bytes in send queue.
542 	@property uint sendBytes() // getter
543 	{
544 		return writebuf.length;
545 	}
546 	
547 	
548 	///
549 	// Number of bytes in recv queue.
550 	@property uint receiveBytes() // getter
551 	{
552 		return rpos;
553 	}
554 	
555 	
556 	///
557 	// Same signature as RegisterEventCallback for simplicity.
558 	void event(DflSocket _sock, EventType type, int err)
559 	in
560 	{
561 		assert(_sock is sock);
562 	}
563 	body
564 	{
565 		switch(type)
566 		{
567 			case EventType.READ:
568 				readEvent();
569 				break;
570 			
571 			case EventType.WRITE:
572 				writeEvent();
573 				break;
574 			
575 			default:
576 		}
577 	}
578 	
579 	
580 	///
581 	// Call on a read event so that incoming data may be buffered.
582 	void readEvent()
583 	{
584 		if(readbuf.length - rpos < 1024)
585 			readbuf.length = readbuf.length + 2048;
586 		
587 		int rd = sock.receive(readbuf[rpos .. readbuf.length]);
588 		if(rd > 0)
589 			rpos += cast(uint)rd;
590 	}
591 	
592 	
593 	///
594 	// Call on a write event so that buffered outgoing data may be sent.
595 	void writeEvent()
596 	{
597 		if(writebuf.length)
598 		{
599 			ubyte[] buf;
600 			
601 			if(writebuf.length > 4096)
602 				buf = writebuf[0 .. 4096];
603 			else
604 				buf = writebuf;
605 			
606 			int st = sock.send(buf);
607 			if(st > 0)
608 				writebuf = writebuf[st .. writebuf.length];
609 		}
610 		else
611 		{
612 			//canwrite = true;
613 		}
614 	}
615 	
616 	
617 	deprecated
618 	{
619 		alias receiveBytes recvBytes;
620 		alias receive recv;
621 	}
622 	
623 	
624 	private:
625 	ubyte[] writebuf;
626 	ubyte[] readbuf;
627 	uint rpos;
628 	DflSocket sock;
629 	//bool canwrite = false;
630 	
631 	
632 	@property bool canwrite() // getter
633 	{
634 		return writebuf.length == 0;
635 	}
636 }
637 
638 
639 private:
640 
641 struct EventInfo
642 {
643 	DflSocket sock;
644 	RegisterEventCallback callback;
645 	DThrowable exception;
646 }
647 
648 
649 enum UINT WM_DFL_NETEVENT = WM_USER + 104;
650 enum UINT WM_DFL_HOSTEVENT = WM_USER + 105;
651 enum NETEVENT_CLASSNAME = "DFL_NetEvent";
652 
653 EventInfo[socket_t] allEvents;
654 GetHost[HANDLE] allGetHosts;
655 HWND hwNet;
656 
657 
658 extern(Windows) LRESULT netWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow
659 {
660 	switch(msg)
661 	{
662 		case WM_DFL_NETEVENT:
663 			if(cast(socket_t)wparam in allEvents)
664 			{
665 				EventInfo ei = allEvents[cast(socket_t)wparam];
666 				try
667 				{
668 					ei.callback(ei.sock, cast(EventType)LOWORD(lparam), HIWORD(lparam));
669 				}
670 				catch (DThrowable e)
671 				{
672 					ei.exception = e;
673 				}
674 			}
675 			break;
676 		
677 		case WM_DFL_HOSTEVENT:
678 			if(cast(HANDLE)wparam in allGetHosts)
679 			{
680 				GetHost gh;
681 				gh = allGetHosts[cast(HANDLE)wparam];
682 				assert(gh !is null);
683 				//delete allGetHosts[cast(HANDLE)wparam];
684 				allGetHosts.remove(cast(HANDLE)wparam);
685 				try
686 				{
687 					gh._gotEvent(lparam);
688 				}
689 				catch (DThrowable e)
690 				{
691 					gh.exception = e;
692 				}
693 			}
694 			break;
695 		
696 		default:
697 	}
698 	
699 	return 1;
700 }
701 
702 
703 void _init()
704 {
705 	WNDCLASSEXA wce;
706 	wce.cbSize = wce.sizeof;
707 	wce.lpszClassName = NETEVENT_CLASSNAME.ptr;
708 	wce.lpfnWndProc = &netWndProc;
709 	wce.hInstance = GetModuleHandleA(null);
710 	
711 	if(!RegisterClassExA(&wce))
712 	{
713 		debug(APP_PRINT)
714 			cprintf("RegisterClassEx() failed for network event class.\n");
715 		
716 		init_err:
717 		throw new DflException("Unable to initialize asynchronous socket library");
718 	}
719 	
720 	hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, HWND_MESSAGE, null, wce.hInstance, null);
721 	if(!hwNet)
722 	{
723 		// Guess it doesn't support HWND_MESSAGE, so just try null parent.
724 		
725 		hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, null, null, wce.hInstance, null);
726 		if(!hwNet)
727 		{
728 			debug(APP_PRINT)
729 				cprintf("CreateWindowEx() failed for network event window.\n");
730 			
731 			goto init_err;
732 		}
733 	}
734 }
735 
736 }
737