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