[project @ 1998-02-02 17:27:26 by simonm]
[ghc-hetmet.git] / ghc / lib / misc / Socket.lhs
diff --git a/ghc/lib/misc/Socket.lhs b/ghc/lib/misc/Socket.lhs
new file mode 100644 (file)
index 0000000..34e99b6
--- /dev/null
@@ -0,0 +1,192 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1995, 1996
+%
+% Last Modified: Fri Jul 21 15:53:32 1995
+% Darren J Moffat <moffatd@dcs.gla.ac.uk>
+%
+% Further hacked on by Sigbjorn Finne <sof@dcs.gla.ac.uk>
+%
+\section[Socket]{Haskell 1.3 Socket bindings}
+
+
+\begin{code}       
+{-# OPTIONS -#include "cbits/ghcSockets.h" #-}
+
+#include "config.h"
+
+module Socket (
+        PortID(..),
+       Hostname,
+
+       connectTo,      -- :: Hostname -> PortID -> IO Handle
+       listenOn,       -- :: PortID -> IO Socket
+       
+       accept,         -- :: Socket -> IO (Handle, HostName)
+
+       sendTo,         -- :: Hostname -> PortID -> String -> IO ()
+       recvFrom,       -- :: Hostname -> PortID -> IO String
+
+       socketPort      -- :: Socket -> IO PortID
+
+       ) where
+
+import BSD
+import SocketPrim hiding ( accept, socketPort )
+import qualified SocketPrim ( accept, socketPort )
+import IO
+\end{code} 
+
+%***************************************************************************
+%*                                                                         *
+\subsection[Socket-Setup]{High Level ``Setup'' functions}
+%*                                                                         *
+%***************************************************************************
+
+Calling $connectTo$ creates a client side socket which is
+connected to the given host and port.  The Protocol and socket type is
+derived from the given port identifier.  If a port number is given
+then the result is always an internet family $Stream$ socket. 
+
+If the $PortID$ specifies a unix family socket and the $Hostname$
+differs from that returned by $getHostname$ then an error is
+raised. Alternatively an empty string may be given to $connectTo$
+signalling that the current hostname applies.
+
+\begin{code}
+data PortID = 
+         Service String                -- Service Name eg "ftp"
+       | PortNumber Int                -- User defined Port Number
+#ifndef cygwin32_TARGET_OS
+       | UnixSocket String             -- Unix family socket in file system
+#endif
+
+type Hostname = String
+-- Maybe consider this alternative.
+-- data Hostname = Name String | IP Int Int Int Int
+\end{code}
+   
+If more control over the socket type is required then $socketPrim$
+should be used instead.
+
+\begin{code}
+connectTo :: Hostname          -- Hostname
+         -> PortID             -- Port Identifier
+         -> IO Handle          -- Connected Socket
+
+connectTo hostname (Service serv) =
+    getProtocolNumber "tcp"                        >>= \ proto ->
+    socket AF_INET Stream proto                            >>= \ sock ->
+    getServicePortNumber serv                      >>= \ port ->
+    getHostByName hostname                         >>= \ (HostEntry _ _ _ haddrs) ->
+    connect sock (SockAddrInet port (head haddrs))  >>
+    socketToHandle sock        ReadWriteMode               >>= \ h ->
+    return h
+connectTo hostname (PortNumber port) =
+    getProtocolNumber "tcp"                        >>= \ proto ->
+    socket AF_INET Stream proto                            >>= \ sock ->
+    getHostByName hostname                         >>= \ (HostEntry _ _ _ haddrs) ->
+    connect sock (SockAddrInet port (head haddrs))  >>
+    socketToHandle sock ReadWriteMode
+
+#ifndef cygwin32_TARGET_OS
+connectTo _ (UnixSocket path) =
+    socket AF_UNIX Datagram 0                      >>= \ sock ->
+    connect sock (SockAddrUnix path)               >>
+    socketToHandle sock ReadWriteMode
+#endif
+
+\end{code}
+
+The dual to the $connectTo$ call. This creates the server side
+socket which has been bound to the specified port.
+
+\begin{code}
+listenOn :: PortID     -- Port Identifier
+        -> IO Socket   -- Connected Socket
+
+listenOn (Service serv) =
+    getProtocolNumber "tcp"                        >>= \ proto ->
+    socket AF_INET Stream proto                            >>= \ sock ->
+    getServicePortNumber serv                      >>= \ port ->
+    bindSocket sock (SockAddrInet port iNADDR_ANY)  >>
+    listen sock maxListenQueue                     >>
+    return sock
+listenOn (PortNumber port) =
+    getProtocolNumber "tcp"                        >>= \ proto ->
+    socket AF_INET Stream proto                            >>= \ sock ->
+    bindSocket sock (SockAddrInet port iNADDR_ANY)  >>
+    listen sock maxListenQueue                     >>
+    return sock
+#ifndef cygwin32_TARGET_OS
+listenOn (UnixSocket path) =
+    socket AF_UNIX Datagram 0                      >>= \ sock ->
+    bindSocket sock (SockAddrUnix path)                    >>
+    return sock
+#endif
+\end{code}
+
+\begin{code}
+accept :: Socket               -- Listening Socket
+       -> IO (Handle,          -- StdIO Handle for read/write
+             HostName)         -- HostName of Peer socket
+
+accept sock =
+ SocketPrim.accept sock                     >>= \ (sock', (SockAddrInet _ haddr)) ->
+ getHostByAddr AF_INET haddr         >>= \ (HostEntry peer _ _ _) ->
+ socketToHandle sock ReadWriteMode   >>= \ handle ->
+ return (handle, peer)
+\end{code}
+
+Send and recived data from/to the given host and port number.  These
+should normally only be used where the socket will not be required for
+further calls.
+
+Thse are wrappers around socket, bind, and listen.
+
+\begin{code}
+sendTo :: Hostname     -- Hostname
+       -> PortID       -- Port Number
+       -> String       -- Message to send
+       -> IO ()
+sendTo h p msg = 
+ connectTo h p >>= \ s ->
+ hPutStr s msg >>
+ hClose s
+
+recvFrom :: Hostname   -- Hostname
+        -> PortID      -- Port Number
+        -> IO String   -- Received Data
+recvFrom host port =
+ listenOn port         >>= \ s ->
+ let 
+  waiting =
+   SocketPrim.accept s         >>= \ (s', (SockAddrInet _ haddr)) ->
+   getHostByAddr AF_INET haddr         >>= \ (HostEntry peer _ _ _) ->
+   if peer /= host then
+      sClose s'                        >>
+      waiting
+   else
+      readSocketAll s'         >>= \ msg ->
+      sClose s'                        >>
+      return msg
+ in
+ waiting                       >>= \ message ->
+ sClose s                      >>
+ return message
+\end{code}
+
+Access function returning the port type/id of socket.
+
+\begin{code}
+socketPort :: Socket -> IO PortID
+socketPort s =
+    getSocketName s                    >>= \ sockaddr ->
+    return (case sockaddr of
+               SockAddrInet port _     ->
+                   (PortNumber port)
+#ifndef cygwin32_TARGET_OS
+               SockAddrUnix path       ->
+                   (UnixSocket path)
+#endif
+           )
+\end{code}