niti: fix type in tool description
[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 # Portal for communication between two machines
23 class Socket
24 super BufferedIStream
25 super OStream
26 super PollableIStream
27
28 # IPv4 address the socket is connected to
29 # Formatted as xxx.xxx.xxx.xxx
30 var address: String
31
32 # Hostname of the socket connected to self
33 # In C : The real canonical host name (e.g. example.org)
34 var host: nullable String
35
36 # Port open for the socket
37 var port: Int
38
39 # Underlying C socket
40 private var socket: FFSocket
41
42 # Underlying C socket
43 private var addrin: FFSocketAddrIn
44
45 redef var end_reached = false
46
47 # Creates a socket connection to host `thost` on port `port`
48 init client(thost: String, tport: Int)
49 do
50 _buffer = new FlatBuffer
51 _buffer_pos = 0
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 end_reached = true
55 return
56 end
57 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
58 var hostname = socket.gethostbyname(thost)
59 addrin = new FFSocketAddrIn.with_hostent(hostname, tport)
60 address = addrin.address
61 host = hostname.h_name
62 port = addrin.port
63 if not end_reached then end_reached = not connect
64 end
65
66 # Creates a server socket on port `tport`, with a connection queue of size `max`
67 init server(tport: Int, max: Int)
68 do
69 _buffer = new FlatBuffer
70 _buffer_pos = 0
71 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
72 if socket.address_is_null then
73 end_reached = true
74 return
75 end
76 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
77 addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet)
78 address = addrin.address
79 port = addrin.port
80 host = null
81 bind
82 listen(max)
83 end
84
85 # Creates a client socket, this is meant to be used by accept only
86 private init primitive_init(h: FFSocketAcceptResult)
87 do
88 _buffer = new FlatBuffer
89 _buffer_pos = 0
90 socket = h.socket
91 addrin = h.addrIn
92 address = addrin.address
93 port = addrin.port
94 host = null
95 end
96
97 redef fun poll_in do return ready_to_read(0)
98
99 # Returns an array containing an enum of the events ready to be read
100 #
101 # event_types : Combination of several event types to watch
102 #
103 # timeout : Time in milliseconds before stopping listening for events on this socket
104 #
105 private fun pollin(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do
106 if end_reached then return new Array[FFSocketPollValues]
107 return socket.socket_poll(new PollFD(socket.descriptor, event_types), timeout)
108 end
109
110 # Easier use of pollin to check for something to read on all channels of any priority
111 #
112 # timeout : Time in milliseconds before stopping to wait for events
113 #
114 fun ready_to_read(timeout: Int): Bool
115 do
116 if _buffer_pos < _buffer.length then return true
117 if eof then return false
118 var events = new Array[FFSocketPollValues]
119 events.push(new FFSocketPollValues.pollin)
120 return pollin(events, timeout).length != 0
121 end
122
123 # Checks if the socket still is connected
124 #
125 fun connected: Bool
126 do
127 if eof then return false
128 var events = new Array[FFSocketPollValues]
129 events.push(new FFSocketPollValues.pollhup)
130 events.push(new FFSocketPollValues.pollerr)
131 if pollin(events, 0).length == 0 then
132 return true
133 else
134 end_reached = true
135 return false
136 end
137 end
138
139 redef fun is_writable do return not end_reached
140
141 # Establishes a connection to socket addrin
142 #
143 # REQUIRES : not self.end_reached
144 private fun connect: Bool
145 do
146 assert not end_reached
147 return socket.connect(addrin) >= 0
148 end
149
150 # If socket.end_reached, nothing will happen
151 redef fun write(msg: Text)
152 do
153 if end_reached then return
154 socket.write(msg.to_s)
155 end
156
157 fun write_ln(msg: Text)
158 do
159 if end_reached then return
160 write(msg.to_s)
161 write("\n")
162 end
163
164 redef fun fill_buffer
165 do
166 _buffer.clear
167 _buffer_pos = 0
168 if not connected then return
169 var read = socket.read
170 if read.length == 0 then
171 close
172 end_reached = true
173 end
174 _buffer.append(read)
175 end
176
177 redef fun close do
178 if end_reached then return
179 if socket.close >= 0 then
180 end_reached = true
181 end
182 end
183
184 # Associates the socket to a local address and port
185 #
186 # Returns : `true` if the socket could be bound, `false` otherwise
187 private fun bind: Bool do
188 if end_reached then return false
189 return socket.bind(addrin) >= 0
190 end
191
192 # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients
193 #
194 # Returns : `true` if the socket could be set, `false` otherwise
195 private fun listen(size: Int): Bool do
196 if end_reached then return false
197 return socket.listen(size) >= 0
198 end
199
200 # Accepts an incoming connection from a client
201 # This creates a new socket that represents the connection to a client
202 #
203 # Returns : the socket for communication with the client
204 #
205 # REQUIRES : not self.end_reached
206 fun accept: Socket do
207 assert not end_reached
208 return new Socket.primitive_init(socket.accept)
209 end
210
211 end
212
213 class SocketSet
214 var sset = new FFSocketSet
215 fun set(s: Socket) do sset.set(s.socket) end
216 fun is_set(s: Socket): Bool do return sset.is_set(s.socket) end
217 fun zero do sset.zero end
218 fun clear(s: Socket) do sset.clear(s.socket) end
219 end
220
221 class SocketObserver
222 private var observer: FFSocketObserver
223 var readset: nullable SocketSet = null
224 var writeset: nullable SocketSet = null
225 var exceptset: nullable SocketSet = null
226 init(read :Bool, write :Bool, except: Bool)
227 do
228 if read then readset = new SocketSet
229 if write then writeset = new SocketSet
230 if except then exceptset = new SocketSet
231 observer = new FFSocketObserver
232 end
233 fun select(max: Socket,seconds: Int, microseconds: Int): Bool
234 do
235 var timeval = new FFTimeval(seconds, microseconds)
236 return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0
237 end
238 end
239