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