lib/socket: minimal update to `SocketObserver` API and note some problems
[nit.git] / lib / socket / socket_c.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Low-level socket functionalities
18 module socket_c
19
20 in "C Header" `{
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <netdb.h>
30 #include <sys/poll.h>
31 `}
32
33 in "C" `{
34 #include <fcntl.h>
35 #include <netinet/tcp.h>
36 `}
37
38 # Wrapper for the data structure PollFD used for polling on a socket
39 class PollFD
40
41 # The PollFD object
42 private var poll_struct: NativeSocketPollFD
43
44 # A collection of the events to be watched
45 var events: Array[NativeSocketPollValues]
46
47 init(pid: Int, events: Array[NativeSocketPollValues])
48 do
49 assert events.length >= 1
50 self.events = events
51
52 var events_in_one = events[0]
53
54 for i in [1 .. events.length-1] do
55 events_in_one += events[i]
56 end
57
58 self.poll_struct = new NativeSocketPollFD(pid, events_in_one)
59 end
60
61 # Reads the response and returns an array with the type of events that have been found
62 private fun check_response(response: Int): Array[NativeSocketPollValues]
63 do
64 var resp_array = new Array[NativeSocketPollValues]
65 for i in events do
66 if c_check_resp(response, i) != 0 then
67 resp_array.push(i)
68 end
69 end
70 return resp_array
71 end
72
73 # Checks if the poll call has returned true for a particular type of event
74 private fun c_check_resp(response: Int, mask: NativeSocketPollValues): Int
75 `{
76 return response & mask;
77 `}
78
79 end
80
81 # Data structure used by the poll function
82 private extern class NativeSocketPollFD `{ struct pollfd `}
83
84 # File descriptor id
85 private fun fd: Int `{ return recv.fd; `}
86
87 # List of events to be watched
88 private fun events: Int `{ return recv.events; `}
89
90 # List of events received by the last poll function
91 private fun revents: Int `{ return recv.revents; `}
92
93 new (pid: Int, events: NativeSocketPollValues) `{
94 struct pollfd poll;
95 poll.fd = pid;
96 poll.events = events;
97 return poll;
98 `}
99
100 end
101
102 extern class NativeSocket `{ int* `}
103
104 new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
105 int ds = socket(domain, socketType, protocol);
106 if(ds == -1){
107 return NULL;
108 }
109 int *d = malloc(sizeof(int));
110 memcpy(d, &ds, sizeof(ds));
111 return d;
112 `}
113
114 fun destroy `{ free(recv); `}
115
116 fun close: Int `{ return close(*recv); `}
117
118 fun descriptor: Int `{ return *recv; `}
119
120 fun gethostbyname(n: String): NativeSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
121
122 fun connect(addrIn: NativeSocketAddrIn): Int `{
123 return connect(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn));
124 `}
125
126 fun write(buffer: String): Int
127 import String.to_cstring, String.length `{
128 return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer));
129 `}
130
131 fun read: String import NativeString.to_s_with_length `{
132 static char c[1024];
133 int n = read(*recv, c, 1024);
134 if(n < 0) {
135 return NativeString_to_s_with_length("",0);
136 }
137 char* ret = malloc(n + 1);
138 memcpy(ret, c, n);
139 ret[n] = '\0';
140 return NativeString_to_s_with_length(ret, n);
141 `}
142
143 # Sets an option for the socket
144 fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int) `{
145 int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int));
146 if(err != 0){
147 perror("Error on setsockopts: ");
148 exit(1);
149 }
150 `}
151
152 fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
153
154 fun listen(size: Int): Int `{ return listen(*recv, size); `}
155
156 # Checks if the buffer is ready for any event specified when creating the pollfd structure
157 fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues]
158 do
159 var result = native_poll(filedesc.poll_struct, timeout)
160 assert result != -1
161 return filedesc.check_response(result)
162 end
163
164 # Call to the poll function of the C socket
165 #
166 # Signature:
167 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
168 #
169 # Official documentation of the poll function:
170 #
171 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
172 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
173 # The number of pollfd structures in the fds array is specified by nfds.
174 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
175 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
176 # It is a pointer to an array with one member for each open file descriptor of interest.
177 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
178 # OR'ing a combination of the pollfd flags.
179 private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{
180 int poll_return = poll(&filedesc, 1, timeout);
181 return poll_return;
182 `}
183
184 private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{
185 socklen_t s = sizeof(struct sockaddr);
186 int socket = accept(*recv, (struct sockaddr*)addr_in, &s);
187 if (socket == -1) return NULL;
188
189 int *ptr = malloc(sizeof(int));
190 *ptr = socket;
191 return ptr;
192 `}
193
194 fun accept: nullable SocketAcceptResult
195 do
196 var addrIn = new NativeSocketAddrIn
197 var s = native_accept(addrIn)
198 if s.address_is_null then return null
199 return new SocketAcceptResult(s, addrIn)
200 end
201
202 # Set wether this socket is non blocking
203 fun non_blocking=(value: Bool) `{
204 int flags = fcntl(*recv, F_GETFL, 0);
205 if (flags == -1) flags = 0;
206
207 if (value) {
208 flags = flags | O_NONBLOCK;
209 } else if (flags & O_NONBLOCK) {
210 flags = flags - O_NONBLOCK;
211 } else {
212 return;
213 }
214 fcntl(*recv, F_SETFL, flags);
215 `}
216 end
217
218 # Result of a call to `NativeSocket::accept`
219 class SocketAcceptResult
220
221 # Opened socket
222 var socket: NativeSocket
223
224 # Address of the remote client
225 var addr_in: NativeSocketAddrIn
226 end
227
228 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
229 new `{
230 struct sockaddr_in *sai = NULL;
231 sai = malloc(sizeof(struct sockaddr_in));
232 return sai;
233 `}
234
235 new with(port: Int, family: NativeSocketAddressFamilies) `{
236 struct sockaddr_in *sai = NULL;
237 sai = malloc(sizeof(struct sockaddr_in));
238 sai->sin_family = family;
239 sai->sin_port = htons(port);
240 sai->sin_addr.s_addr = INADDR_ANY;
241 return sai;
242 `}
243
244 new with_hostent(hostent: NativeSocketHostent, port: Int) `{
245 struct sockaddr_in *sai = NULL;
246 sai = malloc(sizeof(struct sockaddr_in));
247 sai->sin_family = hostent->h_addrtype;
248 sai->sin_port = htons(port);
249 memcpy((char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length);
250 return sai;
251 `}
252
253 fun address: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(recv->sin_addr)); `}
254
255 fun family: NativeSocketAddressFamilies `{ return recv->sin_family; `}
256
257 fun port: Int `{ return ntohs(recv->sin_port); `}
258
259 fun destroy `{ free(recv); `}
260 end
261
262 extern class NativeSocketHostent `{ struct hostent* `}
263 private fun native_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(recv->h_aliases[i]); `}
264
265 private fun native_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `}
266
267 fun h_aliases: Array[String]
268 do
269 var i=0
270 var d=new Array[String]
271 loop
272 d.add(native_h_aliases(i))
273 if native_h_aliases_reachable(i+1) == false then break
274 i += 1
275 end
276 return d
277 end
278
279 fun h_addr: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)recv->h_addr)); `}
280
281 fun h_addrtype: Int `{ return recv->h_addrtype; `}
282
283 fun h_length: Int `{ return recv->h_length; `}
284
285 fun h_name: String import NativeString.to_s `{ return NativeString_to_s(recv->h_name); `}
286 end
287
288 extern class NativeTimeval `{ struct timeval* `}
289 new (seconds: Int, microseconds: Int) `{
290 struct timeval* tv = NULL;
291 tv = malloc(sizeof(struct timeval));
292 tv->tv_sec = seconds;
293 tv->tv_usec = microseconds;
294 return tv;
295 `}
296
297 fun seconds: Int `{ return recv->tv_sec; `}
298
299 fun microseconds: Int `{ return recv->tv_usec; `}
300
301 fun destroy `{ free(recv); `}
302 end
303
304 extern class NativeSocketSet `{ fd_set* `}
305 new `{
306 fd_set *f = NULL;
307 f = malloc(sizeof(fd_set));
308 return f;
309 `}
310
311 fun set(s: NativeSocket) `{ FD_SET(*s, recv); `}
312
313 fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, recv); `}
314
315 fun zero `{ FD_ZERO(recv); `}
316
317 fun clear(s: NativeSocket) `{ FD_CLR(*s, recv); `}
318
319 fun destroy `{ free(recv); `}
320 end
321
322 class NativeSocketObserver
323 # FIXME this implementation is broken. `reads`, `write` and `except`
324 # are boxed objects, passing them to a C function is illegal.
325 fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
326 except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
327 fd_set *rds, *wts, *exs = NULL;
328 struct timeval *tm = NULL;
329 if (reads != NULL) rds = (fd_set*)reads;
330 if (write != NULL) wts = (fd_set*)write;
331 if (except != NULL) exs = (fd_set*)except;
332 if (timeout != NULL) tm = (struct timeval*)timeout;
333 return select(*max, rds, wts, exs, tm);
334 `}
335 end
336
337 extern class NativeSocketTypes `{ int `}
338 new sock_stream `{ return SOCK_STREAM; `}
339 new sock_dgram `{ return SOCK_DGRAM; `}
340 new sock_raw `{ return SOCK_RAW; `}
341 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
342 end
343
344 extern class NativeSocketAddressFamilies `{ int `}
345 new af_null `{ return 0; `}
346
347 # Unspecified
348 new af_unspec `{ return AF_UNSPEC; `}
349
350 # Local to host (pipes)
351 new af_unix `{ return AF_UNIX; `}
352
353 # For backward compatibility
354 new af_local `{ return AF_LOCAL; `}
355
356 # Internetwork: UDP, TCP, etc.
357 new af_inet `{ return AF_INET; `}
358
359 # IBM SNA
360 new af_sna `{ return AF_SNA; `}
361
362 # DECnet
363 new af_decnet `{ return AF_DECnet; `}
364
365 # Internal Routing Protocol
366 new af_route `{ return AF_ROUTE; `}
367
368 # Novell Internet Protocol
369 new af_ipx `{ return AF_IPX; `}
370
371 # Integrated Services Digital Network
372 new af_isdn `{ return AF_ISDN; `}
373
374 # IPv6
375 new af_inet6 `{ return AF_INET6; `}
376
377 new af_max `{ return AF_MAX; `}
378 end
379
380 extern class NativeSocketProtocolFamilies `{ int `}
381 new pf_null `{ return 0; `}
382 new pf_unspec `{ return PF_UNSPEC; `}
383 new pf_local `{ return PF_LOCAL; `}
384 new pf_unix `{ return PF_UNIX; `}
385 new pf_inet `{ return PF_INET; `}
386 new pf_sna `{ return PF_SNA; `}
387 new pf_decnet `{ return PF_DECnet; `}
388 new pf_route `{ return PF_ROUTE; `}
389 new pf_ipx `{ return PF_IPX; `}
390 new pf_isdn `{ return PF_ISDN; `}
391 new pf_key `{ return PF_KEY; `}
392 new pf_inet6 `{ return PF_INET6; `}
393 new pf_max `{ return PF_MAX; `}
394 end
395
396 # Level on which to set options
397 extern class NativeSocketOptLevels `{ int `}
398
399 # Dummy for IP (As defined in C)
400 new ip `{ return IPPROTO_IP;`}
401
402 # Control message protocol
403 new icmp `{ return IPPROTO_ICMP;`}
404
405 # Use TCP
406 new tcp `{ return IPPROTO_TCP; `}
407
408 # Socket level options
409 new socket `{ return SOL_SOCKET; `}
410 end
411
412 # Options for socket, use with setsockopt
413 extern class NativeSocketOptNames `{ int `}
414
415 # Enables debugging information
416 new debug `{ return SO_DEBUG; `}
417
418 # Authorizes the broadcasting of messages
419 new broadcast `{ return SO_BROADCAST; `}
420
421 # Authorizes the reuse of the local address
422 new reuseaddr `{ return SO_REUSEADDR; `}
423
424 # Authorizes the use of keep-alive packets in a connection
425 new keepalive `{ return SO_KEEPALIVE; `}
426
427 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
428 new tcp_nodelay `{ return TCP_NODELAY; `}
429 end
430
431 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
432 extern class NativeSocketPollValues `{ int `}
433
434 # Data other than high-priority data may be read without blocking.
435 new pollin `{ return POLLIN; `}
436
437 # Normal data may be read without blocking.
438 new pollrdnorm `{ return POLLRDNORM; `}
439
440 # Priority data may be read without blocking.
441 new pollrdband `{ return POLLRDBAND; `}
442
443 # High-priority data may be read without blocking.
444 new pollpri `{ return POLLPRI; `}
445
446 # Normal data may be written without blocking.
447 new pollout `{ return POLLOUT; `}
448
449 # Equivalent to POLLOUT
450 new pollwrnorm `{ return POLLWRNORM; `}
451
452 # Priority data may be written.
453 new pollwrband `{ return POLLWRBAND; `}
454
455 # An error has occurred on the device or stream.
456 #
457 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
458 new pollerr `{ return POLLERR; `}
459
460 # The device has been disconnected.
461 #
462 # This event and POLLOUT are mutually-exclusive; a stream can never be
463 # writable if a hangup has occurred. However, this event and POLLIN,
464 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
465 #
466 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
467 new pollhup `{ return POLLHUP; `}
468
469 # The specified fd value is invalid.
470 #
471 # This flag is only valid in the revents member; it shall ignored in the events member.
472 new pollnval `{ return POLLNVAL; `}
473
474 # Combines two NativeSocketPollValues
475 private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{
476 return recv | other;
477 `}
478 end