lib/socket: Socket now subclass of BufferedIStream and OStream
[nit.git] / lib / socket / socket.nit
index e295860..4a5129e 100644 (file)
@@ -19,48 +19,73 @@ module socket
 
 import socket_c
 
+# Portal for communication between two machines
 class Socket
+       super BufferedIStream
+       super OStream
+
+       # IPv4 address the socket is connected to
+       # Formatted as xxx.xxx.xxx.xxx
        var address: String
+
+       # Hostname of the socket connected to self
+       # In C : The real canonical host name (e.g. example.org)
        var host: nullable String
+
+       # Port open for the socket
        var port: Int
+
+       # Underlying C socket
        private var socket: FFSocket
+
+       # Underlying C socket
        private var addrin: FFSocketAddrIn
 
-       # Guard for errors
-       # If the socket could not be created or if the socket was destroyed
-       # before a call needing the socket was made
-       # this flag will be set to false.
-       var still_alive = true # Note : HUGE SUCCESS
+       redef var end_reached = false
 
+       # Creates a socket connection to host `thost` on port `port`
        init stream_with_host(thost: String, tport: Int)
        do
+               _buffer = new FlatBuffer
+               _buffer_pos = 0
                socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
                if socket.address_is_null then
-                       still_alive = false
+                       end_reached = true
                        return
                end
+               socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
                var hostname = socket.gethostbyname(thost)
                addrin = new FFSocketAddrIn.with_hostent(hostname, tport)
                address = addrin.address
                host = hostname.h_name
                port = addrin.port
+               if not end_reached then end_reached = not connect
        end
 
+       # Creates a server socket on port `tport`
        init stream_with_port(tport: Int)
        do
+               _buffer = new FlatBuffer
+               _buffer_pos = 0
                socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
                if socket.address_is_null then
-                       still_alive = false
+                       end_reached = true
                        return
                end
+               socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
                addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet)
                address = addrin.address
                port = addrin.port
                host = null
+               bind
+               listen(max)
        end
 
-       init primitive_init(h: FFSocketAcceptResult)
+       # Creates a client socket, this is meant to be used by accept only
+       private init primitive_init(h: FFSocketAcceptResult)
        do
+               _buffer = new FlatBuffer
+               _buffer_pos = 0
                socket = h.socket
                addrin = h.addrIn
                address = addrin.address
@@ -85,12 +110,10 @@ class Socket
        #
        fun ready_to_read(timeout: Int): Bool
        do
-               if not still_alive then return false
+               if _buffer_pos < _buffer.length then return true
+               if eof then return false
                var events = new Array[FFSocketPollValues]
                events.push(new FFSocketPollValues.pollin)
-               events.push(new FFSocketPollValues.pollrdnorm)
-               events.push(new FFSocketPollValues.pollpri)
-               events.push(new FFSocketPollValues.pollrdband)
                return poll_in(events, timeout).length != 0
        end
 
@@ -98,53 +121,85 @@ class Socket
        #
        fun connected: Bool
        do
-               if not still_alive then return false
+               if eof then return false
                var events = new Array[FFSocketPollValues]
                events.push(new FFSocketPollValues.pollhup)
                events.push(new FFSocketPollValues.pollerr)
                return poll_in(events, 0).length == 0
        end
 
-       fun connect: Bool do
-               assert still_alive
+       redef fun is_writable do return not end_reached
+
+       # Establishes a connection to socket addrin
+       #
+       # REQUIRES : not self.end_reached
+       private fun connect: Bool
+       do
+               assert not end_reached
                return socket.connect(addrin) >= 0
        end
 
-       fun write(msg: String): Bool do
-               if not still_alive then return false
-               return socket.write(msg) >= 0
+       # If socket.end_reached, nothing will happen
+       redef fun write(msg: Text)
+       do
+               if end_reached then return
+               socket.write(msg.to_s)
+       end
+
+       fun write_ln(msg: Text)
+       do
+               if end_reached then return
+               write(msg.to_s)
+               write("\n")
        end
 
-       fun read: String do
-               if not still_alive then return ""
-               return socket.read
+       redef fun fill_buffer
+       do
+               _buffer.clear
+               _buffer_pos = 0
+               if not connected then return
+               var read = socket.read
+               if read.length == 0 then
+                       close
+                       end_reached = true
+               end
+               _buffer.append(read)
        end
 
-       fun close: Bool do
-               if not still_alive then return true
+       redef fun close do
+               if end_reached then return
                if socket.close >= 0 then
-                       still_alive = false
-                       return true
+                       end_reached = true
                end
-               return false
        end
 
-       fun bind: Bool do
-               if not still_alive then return false
+       # Associates the socket to a local address and port
+       #
+       # Returns : `true` if the socket could be bound, `false` otherwise
+       private fun bind: Bool do
+               if end_reached then return false
                return socket.bind(addrin) >= 0
        end
 
-       fun listen(size: Int): Bool do
-               if not still_alive then return false
+       # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients
+       #
+       # Returns : `true` if the socket could be set, `false` otherwise
+       private fun listen(size: Int): Bool do
+               if end_reached then return false
                return socket.listen(size) >= 0
        end
 
+       # Accepts an incoming connection from a client
+       # This creates a new socket that represents the connection to a client
+       #
+       # Returns : the socket for communication with the client
+       #
+       # REQUIRES : not self.end_reached
        fun accept: Socket do
-               assert still_alive
+               assert not end_reached
                return new Socket.primitive_init(socket.accept)
        end
 
-       fun errno: Int do return socket.errno
 end
 
 class SocketSet