lib/socket: Socket now subclass of BufferedIStream and OStream
[nit.git] / lib / socket / socket.nit
index 8919dbc..4a5129e 100644 (file)
@@ -21,6 +21,9 @@ 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
@@ -38,18 +41,16 @@ class Socket
        # 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)
@@ -58,14 +59,17 @@ class Socket
                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)
@@ -73,11 +77,15 @@ class Socket
                address = addrin.address
                port = addrin.port
                host = null
+               bind
+               listen(max)
        end
 
        # 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
@@ -102,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
 
@@ -115,64 +121,71 @@ 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
 
+       redef fun is_writable do return not end_reached
+
        # Establishes a connection to socket addrin
        #
-       # REQUIRES : self.still_alive
-       fun connect: Bool do
-               assert still_alive
+       # REQUIRES : not self.end_reached
+       private fun connect: Bool
+       do
+               assert not end_reached
                return socket.connect(addrin) >= 0
        end
 
-       # Write a message to connected socket
-       #
-       # Returns `true` if the `msg` was sent, `false` otherwise
-       #
-       # If not socket.sill_alive, false will be returned
-       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
 
-       # Read from connected socket
-       #
-       # If the socket is disconnected, an empty string will be returned
-       fun read: String do
-               if not still_alive then return ""
-               return socket.read
+       fun write_ln(msg: Text)
+       do
+               if end_reached then return
+               write(msg.to_s)
+               write("\n")
        end
 
-       # Close connection
-       #
-       # Returns : `true` if the close was successful, `false` otherwise
-       fun close: Bool do
-               if not still_alive then return true
+       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
+
+       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
 
        # Associates the socket to a local address and port
        #
        # Returns : `true` if the socket could be bound, `false` otherwise
-       fun bind: Bool do
-               if not still_alive then return false
+       private fun bind: Bool do
+               if end_reached then return false
                return socket.bind(addrin) >= 0
        end
 
        # 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
-       fun listen(size: Int): Bool do
-               if not still_alive then return false
+       private fun listen(size: Int): Bool do
+               if end_reached then return false
                return socket.listen(size) >= 0
        end
 
@@ -181,9 +194,9 @@ class Socket
        #
        # Returns : the socket for communication with the client
        #
-       # REQUIRES : self.still_alive
+       # REQUIRES : not self.end_reached
        fun accept: Socket do
-               assert still_alive
+               assert not end_reached
                return new Socket.primitive_init(socket.accept)
        end