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