lib/standard: Added errno and strerror bindings to stdlib.
[nit.git] / lib / socket / socket.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 # Socket services
18 module socket
19
20 import socket_c
21
22 class Socket
23 var address: String
24 var host: nullable String
25 var port: Int
26 private var socket: FFSocket
27 private var addrin: FFSocketAddrIn
28
29 # Guard for errors
30 # If the socket could not be created or if the socket was destroyed
31 # before a call needing the socket was made
32 # this flag will be set to false.
33 var still_alive = true # Note : HUGE SUCCESS
34
35 init stream_with_host(thost: String, tport: Int)
36 do
37 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
38 if socket.address_is_null then
39 still_alive = false
40 return
41 end
42 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
43 var hostname = socket.gethostbyname(thost)
44 addrin = new FFSocketAddrIn.with_hostent(hostname, tport)
45 address = addrin.address
46 host = hostname.h_name
47 port = addrin.port
48 end
49
50 init stream_with_port(tport: Int)
51 do
52 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
53 if socket.address_is_null then
54 still_alive = false
55 return
56 end
57 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
58 addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet)
59 address = addrin.address
60 port = addrin.port
61 host = null
62 end
63
64 init primitive_init(h: FFSocketAcceptResult)
65 do
66 socket = h.socket
67 addrin = h.addrIn
68 address = addrin.address
69 port = addrin.port
70 host = null
71 end
72
73 # Returns an array containing an enum of the events ready to be read
74 #
75 # event_types : Combination of several event types to watch
76 #
77 # timeout : Time in milliseconds before stopping listening for events on this socket
78 #
79 private fun poll_in(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do
80 if not still_alive then return new Array[FFSocketPollValues]
81 return socket.socket_poll(new PollFD(socket.descriptor, event_types), timeout)
82 end
83
84 # Easier use of poll_in to check for something to read on all channels of any priority
85 #
86 # timeout : Time in milliseconds before stopping to wait for events
87 #
88 fun ready_to_read(timeout: Int): Bool
89 do
90 if not still_alive then return false
91 var events = new Array[FFSocketPollValues]
92 events.push(new FFSocketPollValues.pollin)
93 events.push(new FFSocketPollValues.pollrdnorm)
94 events.push(new FFSocketPollValues.pollpri)
95 events.push(new FFSocketPollValues.pollrdband)
96 return poll_in(events, timeout).length != 0
97 end
98
99 # Checks if the socket still is connected
100 #
101 fun connected: Bool
102 do
103 if not still_alive then return false
104 var events = new Array[FFSocketPollValues]
105 events.push(new FFSocketPollValues.pollhup)
106 events.push(new FFSocketPollValues.pollerr)
107 return poll_in(events, 0).length == 0
108 end
109
110 fun connect: Bool do
111 assert still_alive
112 return socket.connect(addrin) >= 0
113 end
114
115 fun write(msg: String): Bool do
116 if not still_alive then return false
117 return socket.write(msg) >= 0
118 end
119
120 fun read: String do
121 if not still_alive then return ""
122 return socket.read
123 end
124
125 fun close: Bool do
126 if not still_alive then return true
127 if socket.close >= 0 then
128 still_alive = false
129 return true
130 end
131 return false
132 end
133
134 fun bind: Bool do
135 if not still_alive then return false
136 return socket.bind(addrin) >= 0
137 end
138
139 fun listen(size: Int): Bool do
140 if not still_alive then return false
141 return socket.listen(size) >= 0
142 end
143
144 fun accept: Socket do
145 assert still_alive
146 return new Socket.primitive_init(socket.accept)
147 end
148
149 end
150
151 class SocketSet
152 var sset: FFSocketSet
153 init do sset = new FFSocketSet end
154 fun set(s: Socket) do sset.set(s.socket) end
155 fun is_set(s: Socket): Bool do return sset.is_set(s.socket) end
156 fun zero do sset.zero end
157 fun clear(s: Socket) do sset.clear(s.socket) end
158 end
159
160 class SocketObserver
161 private var observer: FFSocketObserver
162 var readset: nullable SocketSet = null
163 var writeset: nullable SocketSet = null
164 var exceptset: nullable SocketSet = null
165 init(read :Bool, write :Bool, except: Bool)
166 do
167 if read then readset = new SocketSet
168 if write then writeset = new SocketSet
169 if except then exceptset = new SocketSet
170 observer = new FFSocketObserver
171 end
172 fun select(max: Socket,seconds: Int, microseconds: Int): Bool
173 do
174 var timeval = new FFTimeval(seconds, microseconds)
175 return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0
176 end
177 end
178