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