1 module libasync.udp;
2 
3 import libasync.types;
4 
5 import libasync.events;
6 
7 /// Wrapper for a UDP Stream which must be bound to a socket.
8 final nothrow class AsyncUDPSocket
9 {
10 nothrow:
11 private:
12 	EventLoop m_evLoop;
13 	fd_t m_socket;
14 	NetworkAddress m_local;
15 
16 public:
17 	this(EventLoop evl)
18 	in { assert(evl !is null); }
19 	body { m_evLoop = evl; }
20 
21 	mixin DefStatus;
22 
23 	
24 	/// Returns the locally bound address as an OS-specific structure. 
25 	@property NetworkAddress local() const
26 	{
27 		return m_local;
28 	}
29 
30 	/// Grants broadcast permissions to the socket (must be set before run).
31 	bool broadcast(bool b) 
32 	in { assert(m_socket == fd_t.init, "Cannot change state on unbound UDP socket"); }	
33 	body {
34 		return m_evLoop.broadcast(m_socket, b);
35 	}
36 	
37 	/// Sets the hostname and port to which the UDP socket must be bound locally.
38 	typeof(this) host(string hostname, size_t port)
39 	in { assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); }
40 	body
41 	{
42 		m_local = m_evLoop.resolveHost(hostname, cast(ushort) port);
43 		return this;
44 	}
45 	
46 	/// Sets the IP and port to which the UDP socket will be bound locally.
47 	typeof(this) ip(string ip, size_t port)
48 	in { assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); }
49 	body {
50 		m_local = m_evLoop.resolveIP(ip, cast(ushort) port);
51 		return this;
52 	}
53 
54 	/// Sets the local network address to which this UDP Socket will be bound.
55 	@property void local(NetworkAddress l) 
56 	in { 
57 		assert(l != NetworkAddress.init, "The local address is empty");
58 		assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); 
59 	}
60 	body {
61 		m_local = l;
62 	}
63 
64 	/// Registers the UDP socket in the underlying OS event loop, forwards
65 	/// all related events to the specified delegate.
66 	bool run(void delegate(UDPEvent) del) 
67 	{
68 		UDPHandler handler;
69 		handler.del = del;
70 		handler.conn = this;
71 		return run(handler);
72 	}
73 	
74 	private bool run(UDPHandler del)
75 	in { assert(m_local != NetworkAddress.init && m_socket == fd_t.init, "Cannot rebind an UDP socket"); }
76 	body {
77 		m_socket = m_evLoop.run(this, del);
78 		if (m_socket == fd_t.init)
79 			return false;
80 		else
81 			return true;
82 	}
83 
84 	/// Receives data from one peer and copies its address to the
85 	/// associated OS-specific address structure.
86 	uint recvFrom(ref ubyte[] data, ref NetworkAddress addr) {
87 		return m_evLoop.recvFrom(m_socket, data, addr);
88 	}
89 
90 	/// Sends data to the internet address specified by the associated
91 	/// OS-specific structure.
92 	uint sendTo(in ubyte[] data, in NetworkAddress addr) {
93 		return m_evLoop.sendTo(m_socket, data, addr);
94 	}
95 
96 	/// Cleans up the resources associated with this object in the underlying OS.
97 	bool kill()
98 	in { assert(m_socket != fd_t.init); }
99 	body {
100 		return m_evLoop.kill(this);
101 	}
102 
103 package:
104 	version(Posix) mixin EvInfoMixins;
105 
106 	@property fd_t socket() const {
107 		return m_socket;
108 	}
109 
110 	@property void socket(fd_t val) {
111 		m_socket = val;
112 	}
113 
114 }
115 
116 package struct UDPHandler {
117 	AsyncUDPSocket conn;
118 	void delegate(UDPEvent) del;
119 	void opCall(UDPEvent code){
120 		assert(conn !is null);
121 		del(code);
122 		assert(conn !is null);
123 		return;
124 	}
125 }
126 
127 enum UDPEvent : char {
128 	ERROR = 0,
129 	READ, 
130 	WRITE
131 }