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