Browse Source

Targets distributed into their own modules.

Marcos Dumay de Medeiros 8 years ago
parent
commit
e053c4bf72

+ 119 - 2
src/System/IO/Uniform.hs

@@ -1,8 +1,125 @@
+{-# LANGUAGE ExistentialQuantification #-}
+-- {-# LANGUAGE OverloadedStrings #-}
+-- {-# LANGUAGE ForeignFunctionInterface #-}
+-- {-# LANGUAGE InterruptibleFFI #-}
+-- {-# LANGUAGE EmptyDataDecls #-}
+
 -- |
 -- |
 -- Uniform-IO provides a typeclass for uniform access of different types of targets,
 -- Uniform-IO provides a typeclass for uniform access of different types of targets,
 -- and implementations for abstracting standard streams, files and network connections.
 -- and implementations for abstracting standard streams, files and network connections.
 -- This module also provides TLS wraping over other IO targets.
 -- This module also provides TLS wraping over other IO targets.
-module System.IO.Uniform (module System.IO.Uniform.Targets) where
+module System.IO.Uniform (
+  UniformIO(..),
+  TlsSettings(..),
+  SomeIO(..), TlsIO,
+  mapOverInput
+  ) where
+
+import System.IO.Uniform.External
+
+import Foreign
+--import Foreign.C.Types
+--import Foreign.C.String
+import Foreign.C.Error
+--import qualified Data.IP as IP
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+--import qualified Data.ByteString.Lazy as LBS
+--import qualified Data.ByteString.Builder as BSBuild
+--import qualified Data.List as L
+import Control.Exception
+import Control.Applicative ((<$>))
+--import Data.Monoid (mappend)
+--import qualified Network.Socket as Soc
+import System.IO.Error
+--import Control.Concurrent.MVar
+
+import Data.Default.Class
+
+import System.Posix.Types (Fd(..))
+
+-- |
+-- Typeclass for uniform IO targets.
+class UniformIO a where
+  -- | uRead fd n
+  --
+  --  Reads a block of at most n bytes of data from the IO target.
+  --  Reading will block if there's no data available, but will return immediately
+  --  if any amount of data is availble.
+  --
+  --  Must thow System.IO.Error.EOFError if reading beihond EOF.
+  uRead  :: a -> Int -> IO ByteString
+  -- | uPut fd text
+  --
+  --  Writes all the bytes of text into the IO target. Takes care of retrying if needed.
+  uPut   :: a -> ByteString -> IO ()
+  -- | fClose fd
+  --
+  --  Closes the IO target, releasing any allocated resource. Resources may leak if not called
+  --  for every oppened fd.
+  uClose :: a -> IO ()
+  -- | startTLS fd
+  --
+  --  Starts a TLS connection over the IO target.
+  startTls :: TlsSettings -> a -> IO TlsIO
+  -- | isSecure fd
+  --
+  --  Indicates whether the data written or read from fd is secure at transport.
+  isSecure :: a -> Bool
+  
+-- | A type that wraps any type in the UniformIO class.
+data SomeIO = forall a. (UniformIO a) => SomeIO a
+
+instance UniformIO SomeIO where
+  uRead (SomeIO s) n = uRead s n
+  uPut (SomeIO s) t  = uPut s t
+  uClose (SomeIO s) = uClose s
+  startTls set (SomeIO s) = startTls set s
+  isSecure (SomeIO s) = isSecure s
+
+-- | Settings for starttls functions.
+data TlsSettings = TlsSettings {tlsPrivateKeyFile :: String, tlsCertificateChainFile :: String, tlsDHParametersFile :: String} deriving (Read, Show)
+
+instance Default TlsSettings where
+  def = TlsSettings "" "" ""
+  
+-- | UniformIO wrapper that applies TLS to communication on IO target.
+-- This type is constructed by calling startTls on other targets.
+instance UniformIO TlsIO where
+  uRead s n = do
+    allocaArray n (
+      \b -> do
+        count <- c_recvTls (tls s) b $ fromIntegral n
+        if count < 0
+          then throwErrno "could not read"
+          else BS.packCStringLen (b, fromIntegral count)
+      )
+  uPut s t = do
+    BS.useAsCStringLen t (
+      \(str, n) -> do
+        count <- c_sendTls (tls s) str $ fromIntegral n
+        if count < 0
+          then throwErrno "could not write"
+          else return ()
+      )
+  uClose s = do
+    d <- c_closeTls (tls s)
+    f <- Fd <$> c_prepareToClose d
+    closeFd f
+  startTls _ s = return s
+  isSecure _ = True
 
 
 
 
-import System.IO.Uniform.Targets
+-- | mapOverInput io block_size f initial
+--   Reads io untill the end of file, evaluating a(i) <- f a(i-1) read_data
+--   where a(0) = initial and the last value after io reaches EOF is returned.
+--
+--   Notice that the length of read_data might not be equal block_size.
+mapOverInput :: forall a io. UniformIO io => io -> Int -> (a -> ByteString -> IO a) -> a -> IO a
+mapOverInput io block f initial = do
+  a <- tryIOError $ uRead io block
+  case a of
+    Left e -> if isEOFError e then return initial else throw e -- EOF
+    Right dt -> do
+      i <- f initial dt
+      mapOverInput io block f i

+ 74 - 0
src/System/IO/Uniform/ByteString.hs

@@ -0,0 +1,74 @@
+-- {-# LANGUAGE OverloadedStrings #-}
+-- {-# LANGUAGE ExistentialQuantification #-}
+-- {-# LANGUAGE ForeignFunctionInterface #-}
+-- {-# LANGUAGE InterruptibleFFI #-}
+-- {-# LANGUAGE EmptyDataDecls #-}
+
+module System.IO.Uniform.ByteString (
+  ByteStringIO,
+  withByteStringIO, withByteStringIO'
+  ) where
+
+import System.IO.Uniform
+import System.IO.Uniform.External
+
+import Foreign
+--import Foreign.C.Types
+--import Foreign.C.String
+--import Foreign.C.Error
+-- import qualified Data.IP as IP
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Builder as BSBuild
+--import qualified Data.List as L
+--import Control.Exception
+--import Control.Applicative ((<$>))
+import Data.Monoid (mappend)
+--import qualified Network.Socket as Soc
+import System.IO.Error
+import Control.Concurrent.MVar
+
+--import Data.Default.Class
+
+--import System.Posix.Types (Fd(..))
+
+-- | Wrapper that does UniformIO that reads and writes on the memory.
+data ByteStringIO = ByteStringIO {bsioinput :: MVar (ByteString, Bool), bsiooutput :: MVar BSBuild.Builder}
+instance UniformIO ByteStringIO where
+  uRead s n = do
+    (i, eof) <- takeMVar . bsioinput $ s
+    if eof
+    then do
+      putMVar (bsioinput s) (i, eof)
+      ioError $ mkIOError eofErrorType "read past end of input" Nothing Nothing
+    else do
+      let (r, i') = BS.splitAt n i
+      let eof' = (BS.null r && n > 0)
+      putMVar (bsioinput s) (i', eof')
+      return r
+  uPut s t = do
+    o <- takeMVar . bsiooutput $ s
+    let o' = mappend o $ BSBuild.byteString t
+    putMVar (bsiooutput s) o'
+  uClose _ = return ()
+  startTls _ _ = return . TlsIO $ nullPtr
+  isSecure _ = True
+
+-- | withByteStringIO input f
+--   Runs f with a ByteStringIO that has the given input, returns f's output and
+--   the ByteStringIO output.
+withByteStringIO :: ByteString -> (ByteStringIO -> IO a) -> IO (a, LBS.ByteString)
+withByteStringIO input f = do
+  ivar <- newMVar (input, False)
+  ovar <- newMVar . BSBuild.byteString $ BS.empty
+  let bsio = ByteStringIO ivar ovar
+  a <- f bsio
+  out <- takeMVar . bsiooutput $ bsio
+  return (a, BSBuild.toLazyByteString out)
+
+-- | The same as withByteStringIO, but returns an strict ByteString
+withByteStringIO' :: ByteString -> (ByteStringIO -> IO a) -> IO (a, ByteString)
+withByteStringIO' input f = do
+  (a, t) <- withByteStringIO input f
+  return (a, LBS.toStrict t)

+ 54 - 0
src/System/IO/Uniform/External.hs

@@ -0,0 +1,54 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE InterruptibleFFI #-}
+{-# LANGUAGE EmptyDataDecls #-}
+
+module System.IO.Uniform.External where
+
+import Foreign
+import Foreign.C.Types
+import Foreign.C.String
+
+import System.Posix.Types (Fd(..))
+
+data Nethandler
+-- | A bounded IP port from where to accept SocketIO connections.
+newtype BoundedPort = BoundedPort {lis :: (Ptr Nethandler)}
+data Ds
+newtype SocketIO = SocketIO {sock :: (Ptr Ds)}
+newtype FileIO = FileIO {fd :: (Ptr Ds)}
+data TlsDs
+newtype TlsIO = TlsIO {tls :: (Ptr TlsDs)}
+data StdIO
+
+closeFd :: Fd -> IO ()
+closeFd (Fd f) = c_closeFd f
+            
+-- | Closes a BoundedPort, and releases any resource used by it.
+closePort :: BoundedPort -> IO ()
+closePort p = c_closePort (lis p)
+
+
+foreign import ccall interruptible "getPort" c_getPort :: CInt -> IO (Ptr Nethandler)
+foreign import ccall interruptible "createFromHandler" c_accept :: Ptr Nethandler -> IO (Ptr Ds)
+foreign import ccall safe "createFromFileName" c_createFile :: CString -> IO (Ptr Ds)
+foreign import ccall interruptible "createToIPv4Host" c_connect4 :: CUInt -> CInt -> IO (Ptr Ds)
+foreign import ccall interruptible "createToIPv6Host" c_connect6 :: Ptr CUChar -> CInt -> IO (Ptr Ds)
+
+foreign import ccall interruptible "startSockTls" c_startSockTls :: Ptr Ds -> CString -> CString -> CString -> IO (Ptr TlsDs)
+foreign import ccall safe "getPeer" c_getPeer :: Ptr Ds -> Ptr CUInt -> Ptr CUChar -> Ptr CInt -> IO (CInt)
+
+--foreign import ccall safe "getFd" c_getFd :: Ptr Ds -> IO CInt
+--foreign import ccall safe "getTlsFd" c_getTlsFd :: Ptr TlsDs -> IO CInt
+foreign import ccall safe "closeFd" c_closeFd :: CInt -> IO ()
+
+foreign import ccall safe "prepareToClose" c_prepareToClose :: Ptr Ds -> IO CInt
+foreign import ccall safe "closeHandler" c_closePort :: Ptr Nethandler -> IO ()
+foreign import ccall safe "closeTls" c_closeTls :: Ptr TlsDs -> IO (Ptr Ds)
+
+foreign import ccall interruptible "sendDs" c_send :: Ptr Ds -> Ptr CChar -> CInt -> IO CInt
+foreign import ccall interruptible "stdDsSend" c_sendStd :: Ptr CChar -> CInt -> IO CInt
+foreign import ccall interruptible "tlsDsSend" c_sendTls :: Ptr TlsDs -> Ptr CChar -> CInt -> IO CInt
+
+foreign import ccall interruptible "recvDs" c_recv :: Ptr Ds -> Ptr CChar -> CInt -> IO CInt
+foreign import ccall interruptible "stdDsRecv" c_recvStd :: Ptr CChar -> CInt -> IO CInt
+foreign import ccall interruptible "tlsDsRecv" c_recvTls :: Ptr TlsDs -> Ptr CChar -> CInt -> IO CInt

+ 72 - 0
src/System/IO/Uniform/File.hs

@@ -0,0 +1,72 @@
+-- {-# LANGUAGE OverloadedStrings #-}
+-- {-# LANGUAGE ExistentialQuantification #-}
+-- {-# LANGUAGE ForeignFunctionInterface #-}
+-- {-# LANGUAGE InterruptibleFFI #-}
+-- {-# LANGUAGE EmptyDataDecls #-}
+
+module System.IO.Uniform.File (
+  FileIO,
+  openFile
+  ) where
+
+import System.IO.Uniform
+import System.IO.Uniform.External
+
+import Foreign
+--import Foreign.C.Types
+import Foreign.C.String
+import Foreign.C.Error
+--import qualified Data.IP as IP
+--import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+--import qualified Data.ByteString.Lazy as LBS
+--import qualified Data.ByteString.Builder as BSBuild
+--import qualified Data.List as L
+--import Control.Exception
+import Control.Applicative ((<$>))
+--import Data.Monoid (mappend)
+--import qualified Network.Socket as Soc
+--import System.IO.Error
+--import Control.Concurrent.MVar
+
+--import Data.Default.Class
+
+import System.Posix.Types (Fd(..))
+
+
+-- | UniformIO type for file IO.
+instance UniformIO FileIO where
+  uRead s n = do
+    allocaArray n (
+      \b -> do
+        count <- c_recv (fd s) b $ fromIntegral n
+        if count < 0
+          then throwErrno "could not read"
+          else  BS.packCStringLen (b, fromIntegral count)
+      )
+  uPut s t = do
+    BS.useAsCStringLen t (
+      \(str, n) -> do
+        count <- c_send (fd s) str $ fromIntegral n
+        if count < 0
+          then throwErrno "could not write"
+          else return ()
+      )
+  uClose s = do
+    f <- Fd <$> c_prepareToClose (fd s)
+    closeFd f
+  -- Not implemented yet.
+  startTls _ _ = return . TlsIO $ nullPtr
+  isSecure _ = False
+  
+  
+-- | Open a file for bidirectional IO.
+openFile :: String -> IO FileIO
+openFile fileName = do
+  r <- withCString fileName (
+    \f -> fmap FileIO $ c_createFile f
+    )
+  if fd r == nullPtr
+    then throwErrno "could not open file"
+    else return r
+

+ 166 - 0
src/System/IO/Uniform/Network.hs

@@ -0,0 +1,166 @@
+-- {-# LANGUAGE OverloadedStrings #-}
+-- {-# LANGUAGE ExistentialQuantification #-}
+-- {-# LANGUAGE ForeignFunctionInterface #-}
+-- {-# LANGUAGE InterruptibleFFI #-}
+-- {-# LANGUAGE EmptyDataDecls #-}
+
+module System.IO.Uniform.Network (
+  SocketIO,
+  BoundedPort,
+  connectTo,
+  connectToHost,
+  bindPort,
+  accept,
+  closePort,
+  getPeer
+  ) where
+
+import System.IO.Uniform
+import System.IO.Uniform.External
+
+import Foreign
+import Foreign.C.Types
+import Foreign.C.String
+import Foreign.C.Error
+import qualified Data.IP as IP
+--import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+--import qualified Data.ByteString.Lazy as LBS
+--import qualified Data.ByteString.Builder as BSBuild
+import qualified Data.List as L
+import Control.Exception
+import Control.Applicative ((<$>))
+--import Data.Monoid (mappend)
+import qualified Network.Socket as Soc
+import System.IO.Error
+--import Control.Concurrent.MVar
+
+--import Data.Default.Class
+
+import System.Posix.Types (Fd(..))
+
+-- | UniformIO IP connections.
+instance UniformIO SocketIO where
+  uRead s n = do
+    allocaArray n (
+      \b -> do
+        count <- c_recv (sock s) b (fromIntegral n)
+        if count < 0
+          then throwErrno "could not read"
+          else BS.packCStringLen (b, fromIntegral count)
+      )
+  uPut s t = do
+    BS.useAsCStringLen t (
+      \(str, n) -> do
+        count <- c_send (sock s) str $ fromIntegral n
+        if count < 0
+          then throwErrno "could not write"
+          else return ()
+      )
+  uClose s = do
+    f <- Fd <$> c_prepareToClose (sock s)
+    closeFd f
+  startTls st s = withCString (tlsCertificateChainFile st) (
+    \cert -> withCString (tlsPrivateKeyFile st) (
+      \key -> withCString (tlsDHParametersFile st) (
+        \para -> do
+          r <- c_startSockTls (sock s) cert key para
+          if r == nullPtr
+            then throwErrno "could not start TLS"
+            else return . TlsIO $ r
+        )
+      )
+    )
+  isSecure _ = False
+
+
+-- | connectToHost hostName port
+--
+--  Connects to the given host and port.
+connectToHost :: String -> Int -> IO SocketIO
+connectToHost host port = do
+  ip <- getAddr
+  connectTo ip port
+  where
+    getAddr :: IO IP.IP
+    getAddr = do
+      add <- Soc.getAddrInfo Nothing (Just host) Nothing
+      case add of
+        [] -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
+        (a:_) -> case Soc.addrAddress a of
+          Soc.SockAddrInet _ a'  -> return . IP.IPv4 . IP.fromHostAddress $ a'
+          Soc.SockAddrInet6 _ _ a' _ -> return . IP.IPv6 . IP.fromHostAddress6 $ a'
+          _ -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
+
+
+-- | ConnecctTo ipAddress port
+--
+--  Connects to the given port of the host at the given IP address.
+connectTo :: IP.IP -> Int -> IO SocketIO
+connectTo host port = do
+  r <- case host of
+    IP.IPv4 host' -> fmap SocketIO $ c_connect4 (fromIntegral . IP.toHostAddress $ host') (fromIntegral port)
+    IP.IPv6 host' -> fmap SocketIO $ withArray (ipToArray host') (
+      \add -> c_connect6 add (fromIntegral port)
+      )
+  if sock r == nullPtr
+    then throwErrno "could not connect to host"
+    else return r
+  where
+    ipToArray :: IP.IPv6 -> [CUChar]
+    ipToArray ip = let
+      (w0, w1, w2, w3) = IP.toHostAddress6 ip
+      in L.concat [wtoc w0, wtoc w1, wtoc w2, wtoc w3]
+    wtoc :: Word32 -> [CUChar]
+    wtoc w = let
+      c0 = fromIntegral $ mod w 256
+      w1 = div w 256
+      c1 = fromIntegral $ mod w1 256
+      w2 = div w1 256
+      c2 = fromIntegral $ mod w2 256
+      c3 = fromIntegral $ div w2 256
+      in [c3, c2, c1, c0]
+  
+-- | bindPort port
+--  Binds to the given IP port, becoming ready to accept connections on it.
+--  Binding to port numbers under 1024 will fail unless performed by the superuser,
+--  once bounded, a process can reduce its privileges and still accept clients on that port.
+bindPort :: Int -> IO BoundedPort
+bindPort port = do
+  r <- fmap BoundedPort $ c_getPort $ fromIntegral port
+  if lis r == nullPtr
+    then throwErrno "could not bind to port"
+    else return r
+  
+-- | accept port
+--
+--  Accept clients on a port previously bound with bindPort.
+accept :: BoundedPort -> IO SocketIO
+accept port = do
+  r <- fmap SocketIO $ c_accept (lis port)
+  if sock r == nullPtr
+    then throwErrno "could not accept connection"
+    else return r
+
+-- | Gets the address of the peer socket of a internet connection.
+getPeer :: SocketIO -> IO (IP.IP, Int)
+getPeer s = allocaArray 16 (
+  \p6 -> alloca (
+    \p4 -> alloca (
+      \iptype -> do
+        p <- c_getPeer (sock s) p4 p6 iptype
+        if p == -1
+          then throwErrno "could not get peer address"
+          else do
+          iptp <- peek iptype
+          if iptp == 1
+            then do --IPv6
+            add <- peekArray 16 p6
+            return (IP.IPv6 . IP.toIPv6b $ map fromIntegral add, fromIntegral p)
+            else do --IPv4
+            add <- peek p4
+            return (IP.IPv4 . IP.fromHostAddress . fromIntegral $ add, fromIntegral p)
+      )
+    )
+  )
+

+ 54 - 0
src/System/IO/Uniform/Std.hs

@@ -0,0 +1,54 @@
+-- {-# LANGUAGE OverloadedStrings #-}
+-- {-# LANGUAGE ExistentialQuantification #-}
+-- {-# LANGUAGE ForeignFunctionInterface #-}
+-- {-# LANGUAGE InterruptibleFFI #-}
+-- {-# LANGUAGE EmptyDataDecls #-}
+
+module System.IO.Uniform.Std (
+  StdIO
+  ) where
+
+import System.IO.Uniform
+import System.IO.Uniform.External
+
+import Foreign
+--import Foreign.C.Types
+--import Foreign.C.String
+import Foreign.C.Error
+--import qualified Data.IP as IP
+--import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+--import qualified Data.ByteString.Lazy as LBS
+--import qualified Data.ByteString.Builder as BSBuild
+--import qualified Data.List as L
+--import Control.Exception
+--import Control.Applicative ((<$>))
+--import Data.Monoid (mappend)
+--import qualified Network.Socket as Soc
+--import System.IO.Error
+--import Control.Concurrent.MVar
+
+--import Data.Default.Class
+
+--import System.Posix.Types (Fd(..))
+-- | UniformIO that reads from stdin and writes to stdout.
+instance UniformIO StdIO where
+  uRead _ n = do
+    allocaArray n (
+      \b -> do
+        count <- c_recvStd b (fromIntegral n)
+        if count < 0
+          then throwErrno "could not read"
+          else BS.packCStringLen (b, fromIntegral count)
+      )
+  uPut _ t = do
+    BS.useAsCStringLen t (
+      \(str, n) -> do
+        count <- c_sendStd str $ fromIntegral n
+        if count < 0
+          then throwErrno "could not write"
+          else return ()
+      )
+  uClose _ = return ()
+  startTls _ _ = return . TlsIO $ nullPtr
+  isSecure _ = False

+ 5 - 4
src/System/IO/Uniform/Streamline.hs

@@ -6,6 +6,7 @@
 module System.IO.Uniform.Streamline (Streamline, withClient, withServer, withTarget, send, receiveLine, lazyRecieveLine, lazyReceiveN, startTls, runAttoparsec, runAttoparsecAndReturn, isSecure, setTimeout, setEcho) where
 module System.IO.Uniform.Streamline (Streamline, withClient, withServer, withTarget, send, receiveLine, lazyRecieveLine, lazyReceiveN, startTls, runAttoparsec, runAttoparsecAndReturn, isSecure, setTimeout, setEcho) where
 
 
 import qualified System.IO.Uniform as S
 import qualified System.IO.Uniform as S
+import qualified System.IO.Uniform.Network as N
 import System.IO.Uniform (UniformIO, SomeIO(..), TlsSettings)
 import System.IO.Uniform (UniformIO, SomeIO(..), TlsSettings)
 
 
 import Control.Monad.Trans.Class
 import Control.Monad.Trans.Class
@@ -51,7 +52,7 @@ writeF cl l = if echo cl
 --  Connects to the given server port, runs f, and closes the connection.
 --  Connects to the given server port, runs f, and closes the connection.
 withServer :: MonadIO m => Streamline m a -> IP -> Int -> m a
 withServer :: MonadIO m => Streamline m a -> IP -> Int -> m a
 withServer f host port = do
 withServer f host port = do
-  ds <- liftIO $ S.connectTo host port
+  ds <- liftIO $ N.connectTo host port
   (ret, _) <- withTarget' f $ Data (SomeIO ds) defaultTimeout "" False False
   (ret, _) <- withTarget' f $ Data (SomeIO ds) defaultTimeout "" False False
   liftIO $ S.uClose ds
   liftIO $ S.uClose ds
   return ret
   return ret
@@ -59,10 +60,10 @@ withServer f host port = do
 -- | withClient f boundPort
 -- | withClient f boundPort
 --
 --
 --  Accepts a connection at the bound port, runs f and closes the connection.
 --  Accepts a connection at the bound port, runs f and closes the connection.
-withClient :: MonadIO m => (IP -> Int -> Streamline m a) -> S.BoundedPort -> m a
+withClient :: MonadIO m => (IP -> Int -> Streamline m a) -> N.BoundedPort -> m a
 withClient f port = do
 withClient f port = do
-  ds <- liftIO $ S.accept port
-  (peerIp, peerPort) <- liftIO $ S.getPeer ds
+  ds <- liftIO $ N.accept port
+  (peerIp, peerPort) <- liftIO $ N.getPeer ds
   (ret, _) <- withTarget' (f peerIp peerPort) $ Data (SomeIO ds) defaultTimeout "" False False
   (ret, _) <- withTarget' (f peerIp peerPort) $ Data (SomeIO ds) defaultTimeout "" False False
   liftIO $ S.uClose ds
   liftIO $ S.uClose ds
   return ret
   return ret

+ 0 - 385
src/System/IO/Uniform/Targets.hs

@@ -1,385 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE ForeignFunctionInterface #-}
-{-# LANGUAGE InterruptibleFFI #-}
-{-# LANGUAGE EmptyDataDecls #-}
-
-module System.IO.Uniform.Targets (
-  TlsSettings(..),
-  UniformIO(..),
-  SocketIO, FileIO, StdIO, TlsIO, SomeIO(..), ByteStringIO,
-  BoundedPort, connectTo, connectToHost, bindPort, accept, closePort,
-  openFile, getPeer,
-  withByteStringIO, withByteStringIO',
-  mapOverInput)
-       where
-
-import Foreign
-import Foreign.C.Types
-import Foreign.C.String
-import Foreign.C.Error
-import qualified Data.IP as IP
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as LBS
-import qualified Data.ByteString.Builder as BSBuild
-import qualified Data.List as L
-import Control.Exception
-import Control.Applicative ((<$>))
-import Data.Monoid (mappend)
-import qualified Network.Socket as Soc
-import System.IO.Error
-import Control.Concurrent.MVar
-
-import Data.Default.Class
-
-import System.Posix.Types (Fd(..))
-
--- | Settings for starttls functions.
-data TlsSettings = TlsSettings {tlsPrivateKeyFile :: String, tlsCertificateChainFile :: String, tlsDHParametersFile :: String} deriving (Read, Show)
-
-instance Default TlsSettings where
-  def = TlsSettings "" "" ""
-
--- |
--- Typeclass for uniform IO targets.
-class UniformIO a where
-  -- | uRead fd n
-  --
-  --  Reads a block of at most n bytes of data from the IO target.
-  --  Reading will block if there's no data available, but will return immediately
-  --  if any amount of data is availble.
-  --
-  --  Must thow System.IO.Error.EOFError if reading beihond EOF.
-  uRead  :: a -> Int -> IO ByteString
-  -- | uPut fd text
-  --
-  --  Writes all the bytes of text into the IO target. Takes care of retrying if needed.
-  uPut   :: a -> ByteString -> IO ()
-  -- | fClose fd
-  --
-  --  Closes the IO target, releasing any allocated resource. Resources may leak if not called
-  --  for every oppened fd.
-  uClose :: a -> IO ()
-  -- | startTLS fd
-  --
-  --  Starts a TLS connection over the IO target.
-  startTls :: TlsSettings -> a -> IO TlsIO
-  -- | isSecure fd
-  --
-  --  Indicates whether the data written or read from fd is secure at transport.
-  isSecure :: a -> Bool
-  
--- | A type that wraps any type in the UniformIO class.
-data SomeIO = forall a. (UniformIO a) => SomeIO a
-
-instance UniformIO SomeIO where
-  uRead (SomeIO s) n = uRead s n
-  uPut (SomeIO s) t  = uPut s t
-  uClose (SomeIO s) = uClose s
-  startTls set (SomeIO s) = startTls set s
-  isSecure (SomeIO s) = isSecure s
-
-data Nethandler
--- | A bounded IP port from where to accept SocketIO connections.
-newtype BoundedPort = BoundedPort {lis :: (Ptr Nethandler)}
-data Ds
-newtype SocketIO = SocketIO {sock :: (Ptr Ds)}
-newtype FileIO = FileIO {fd :: (Ptr Ds)}
-data TlsDs
-newtype TlsIO = TlsIO {tls :: (Ptr TlsDs)}
-data StdIO
-
--- | UniformIO IP connections.
-instance UniformIO SocketIO where
-  uRead s n = do
-    allocaArray n (
-      \b -> do
-        count <- c_recv (sock s) b (fromIntegral n)
-        if count < 0
-          then throwErrno "could not read"
-          else BS.packCStringLen (b, fromIntegral count)
-      )
-  uPut s t = do
-    BS.useAsCStringLen t (
-      \(str, n) -> do
-        count <- c_send (sock s) str $ fromIntegral n
-        if count < 0
-          then throwErrno "could not write"
-          else return ()
-      )
-  uClose s = do
-    f <- Fd <$> c_prepareToClose (sock s)
-    closeFd f
-  startTls st s = withCString (tlsCertificateChainFile st) (
-    \cert -> withCString (tlsPrivateKeyFile st) (
-      \key -> withCString (tlsDHParametersFile st) (
-        \para -> do
-          r <- c_startSockTls (sock s) cert key para
-          if r == nullPtr
-            then throwErrno "could not start TLS"
-            else return . TlsIO $ r
-        )
-      )
-    )
-  isSecure _ = False
-  
--- | UniformIO that reads from stdin and writes to stdout.
-instance UniformIO StdIO where
-  uRead _ n = do
-    allocaArray n (
-      \b -> do
-        count <- c_recvStd b (fromIntegral n)
-        if count < 0
-          then throwErrno "could not read"
-          else BS.packCStringLen (b, fromIntegral count)
-      )
-  uPut _ t = do
-    BS.useAsCStringLen t (
-      \(str, n) -> do
-        count <- c_sendStd str $ fromIntegral n
-        if count < 0
-          then throwErrno "could not write"
-          else return ()
-      )
-  uClose _ = return ()
-  startTls _ _ = return . TlsIO $ nullPtr
-  isSecure _ = False
-  
--- | UniformIO type for file IO.
-instance UniformIO FileIO where
-  uRead s n = do
-    allocaArray n (
-      \b -> do
-        count <- c_recv (fd s) b $ fromIntegral n
-        if count < 0
-          then throwErrno "could not read"
-          else  BS.packCStringLen (b, fromIntegral count)
-      )
-  uPut s t = do
-    BS.useAsCStringLen t (
-      \(str, n) -> do
-        count <- c_send (fd s) str $ fromIntegral n
-        if count < 0
-          then throwErrno "could not write"
-          else return ()
-      )
-  uClose s = do
-    f <- Fd <$> c_prepareToClose (fd s)
-    closeFd f
-  -- Not implemented yet.
-  startTls _ _ = return . TlsIO $ nullPtr
-  isSecure _ = False
-  
--- | UniformIO wrapper that applies TLS to communication on IO target.
--- This type is constructed by calling startTls on other targets.
-instance UniformIO TlsIO where
-  uRead s n = do
-    allocaArray n (
-      \b -> do
-        count <- c_recvTls (tls s) b $ fromIntegral n
-        if count < 0
-          then throwErrno "could not read"
-          else BS.packCStringLen (b, fromIntegral count)
-      )
-  uPut s t = do
-    BS.useAsCStringLen t (
-      \(str, n) -> do
-        count <- c_sendTls (tls s) str $ fromIntegral n
-        if count < 0
-          then throwErrno "could not write"
-          else return ()
-      )
-  uClose s = do
-    d <- c_closeTls (tls s)
-    f <- Fd <$> c_prepareToClose d
-    closeFd f
-  startTls _ s = return s
-  isSecure _ = True
-
--- | Wrapper that does UniformIO that reads and writes on the memory.
-data ByteStringIO = ByteStringIO {bsioinput :: MVar (ByteString, Bool), bsiooutput :: MVar BSBuild.Builder}
-instance UniformIO ByteStringIO where
-  uRead s n = do
-    (i, eof) <- takeMVar . bsioinput $ s
-    if eof
-    then do
-      putMVar (bsioinput s) (i, eof)
-      ioError $ mkIOError eofErrorType "read past end of input" Nothing Nothing
-    else do
-      let (r, i') = BS.splitAt n i
-      let eof' = (BS.null r && n > 0)
-      putMVar (bsioinput s) (i', eof')
-      return r
-  uPut s t = do
-    o <- takeMVar . bsiooutput $ s
-    let o' = mappend o $ BSBuild.byteString t
-    putMVar (bsiooutput s) o'
-  uClose _ = return ()
-  startTls _ _ = return . TlsIO $ nullPtr
-  isSecure _ = True
-
--- | connectToHost hostName port
---
---  Connects to the given host and port.
-connectToHost :: String -> Int -> IO SocketIO
-connectToHost host port = do
-  ip <- getAddr
-  connectTo ip port
-  where
-    getAddr :: IO IP.IP
-    getAddr = do
-      add <- Soc.getAddrInfo Nothing (Just host) Nothing
-      case add of
-        [] -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
-        (a:_) -> case Soc.addrAddress a of
-          Soc.SockAddrInet _ a'  -> return . IP.IPv4 . IP.fromHostAddress $ a'
-          Soc.SockAddrInet6 _ _ a' _ -> return . IP.IPv6 . IP.fromHostAddress6 $ a'
-          _ -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
-
-
--- | ConnecctTo ipAddress port
---
---  Connects to the given port of the host at the given IP address.
-connectTo :: IP.IP -> Int -> IO SocketIO
-connectTo host port = do
-  r <- case host of
-    IP.IPv4 host' -> fmap SocketIO $ c_connect4 (fromIntegral . IP.toHostAddress $ host') (fromIntegral port)
-    IP.IPv6 host' -> fmap SocketIO $ withArray (ipToArray host') (
-      \add -> c_connect6 add (fromIntegral port)
-      )
-  if sock r == nullPtr
-    then throwErrno "could not connect to host"
-    else return r
-  where
-    ipToArray :: IP.IPv6 -> [CUChar]
-    ipToArray ip = let
-      (w0, w1, w2, w3) = IP.toHostAddress6 ip
-      in L.concat [wtoc w0, wtoc w1, wtoc w2, wtoc w3]
-    wtoc :: Word32 -> [CUChar]
-    wtoc w = let
-      c0 = fromIntegral $ mod w 256
-      w1 = div w 256
-      c1 = fromIntegral $ mod w1 256
-      w2 = div w1 256
-      c2 = fromIntegral $ mod w2 256
-      c3 = fromIntegral $ div w2 256
-      in [c3, c2, c1, c0]
-  
--- | bindPort port
---  Binds to the given IP port, becoming ready to accept connections on it.
---  Binding to port numbers under 1024 will fail unless performed by the superuser,
---  once bounded, a process can reduce its privileges and still accept clients on that port.
-bindPort :: Int -> IO BoundedPort
-bindPort port = do
-  r <- fmap BoundedPort $ c_getPort $ fromIntegral port
-  if lis r == nullPtr
-    then throwErrno "could not bind to port"
-    else return r
-  
--- | accept port
---
---  Accept clients on a port previously bound with bindPort.
-accept :: BoundedPort -> IO SocketIO
-accept port = do
-  r <- fmap SocketIO $ c_accept (lis port)
-  if sock r == nullPtr
-    then throwErrno "could not accept connection"
-    else return r
-  
--- | Open a file for bidirectional IO.
-openFile :: String -> IO FileIO
-openFile fileName = do
-  r <- withCString fileName (
-    \f -> fmap FileIO $ c_createFile f
-    )
-  if fd r == nullPtr
-    then throwErrno "could not open file"
-    else return r
-
--- | Gets the address of the peer socket of a internet connection.
-getPeer :: SocketIO -> IO (IP.IP, Int)
-getPeer s = allocaArray 16 (
-  \p6 -> alloca (
-    \p4 -> alloca (
-      \iptype -> do
-        p <- c_getPeer (sock s) p4 p6 iptype
-        if p == -1
-          then throwErrno "could not get peer address"
-          else do
-          iptp <- peek iptype
-          if iptp == 1
-            then do --IPv6
-            add <- peekArray 16 p6
-            return (IP.IPv6 . IP.toIPv6b $ map fromIntegral add, fromIntegral p)
-            else do --IPv4
-            add <- peek p4
-            return (IP.IPv4 . IP.fromHostAddress . fromIntegral $ add, fromIntegral p)
-      )
-    )
-  )
-    
-closeFd :: Fd -> IO ()
-closeFd (Fd f) = c_closeFd f
-            
--- | Closes a BoundedPort, and releases any resource used by it.
-closePort :: BoundedPort -> IO ()
-closePort p = c_closePort (lis p)
-
--- | withByteStringIO input f
---   Runs f with a ByteStringIO that has the given input, returns f's output and
---   the ByteStringIO output.
-withByteStringIO :: ByteString -> (ByteStringIO -> IO a) -> IO (a, LBS.ByteString)
-withByteStringIO input f = do
-  ivar <- newMVar (input, False)
-  ovar <- newMVar . BSBuild.byteString $ BS.empty
-  let bsio = ByteStringIO ivar ovar
-  a <- f bsio
-  out <- takeMVar . bsiooutput $ bsio
-  return (a, BSBuild.toLazyByteString out)
-
--- | The same as withByteStringIO, but returns an strict ByteString
-withByteStringIO' :: ByteString -> (ByteStringIO -> IO a) -> IO (a, ByteString)
-withByteStringIO' input f = do
-  (a, t) <- withByteStringIO input f
-  return (a, LBS.toStrict t)
-
--- | mapOverInput io block_size f initial
---   Reads io untill the end of file, evaluating a(i) <- f a(i-1) read_data
---   where a(0) = initial and the last value after io reaches EOF is returned.
---
---   Notice that the length of read_data might not be equal block_size.
-mapOverInput :: forall a io. UniformIO io => io -> Int -> (a -> ByteString -> IO a) -> a -> IO a
-mapOverInput io block f initial = do
-  a <- tryIOError $ uRead io block
-  case a of
-    Left e -> if isEOFError e then return initial else throw e -- EOF
-    Right dt -> do
-      i <- f initial dt
-      mapOverInput io block f i
-
-
-foreign import ccall interruptible "getPort" c_getPort :: CInt -> IO (Ptr Nethandler)
-foreign import ccall interruptible "createFromHandler" c_accept :: Ptr Nethandler -> IO (Ptr Ds)
-foreign import ccall safe "createFromFileName" c_createFile :: CString -> IO (Ptr Ds)
-foreign import ccall interruptible "createToIPv4Host" c_connect4 :: CUInt -> CInt -> IO (Ptr Ds)
-foreign import ccall interruptible "createToIPv6Host" c_connect6 :: Ptr CUChar -> CInt -> IO (Ptr Ds)
-
-foreign import ccall interruptible "startSockTls" c_startSockTls :: Ptr Ds -> CString -> CString -> CString -> IO (Ptr TlsDs)
-foreign import ccall safe "getPeer" c_getPeer :: Ptr Ds -> Ptr CUInt -> Ptr CUChar -> Ptr CInt -> IO (CInt)
-
---foreign import ccall safe "getFd" c_getFd :: Ptr Ds -> IO CInt
---foreign import ccall safe "getTlsFd" c_getTlsFd :: Ptr TlsDs -> IO CInt
-foreign import ccall safe "closeFd" c_closeFd :: CInt -> IO ()
-
-foreign import ccall safe "prepareToClose" c_prepareToClose :: Ptr Ds -> IO CInt
-foreign import ccall safe "closeHandler" c_closePort :: Ptr Nethandler -> IO ()
-foreign import ccall safe "closeTls" c_closeTls :: Ptr TlsDs -> IO (Ptr Ds)
-
-foreign import ccall interruptible "sendDs" c_send :: Ptr Ds -> Ptr CChar -> CInt -> IO CInt
-foreign import ccall interruptible "stdDsSend" c_sendStd :: Ptr CChar -> CInt -> IO CInt
-foreign import ccall interruptible "tlsDsSend" c_sendTls :: Ptr TlsDs -> Ptr CChar -> CInt -> IO CInt
-
-foreign import ccall interruptible "recvDs" c_recv :: Ptr Ds -> Ptr CChar -> CInt -> IO CInt
-foreign import ccall interruptible "stdDsRecv" c_recvStd :: Ptr CChar -> CInt -> IO CInt
-foreign import ccall interruptible "tlsDsRecv" c_recvTls :: Ptr TlsDs -> Ptr CChar -> CInt -> IO CInt

+ 5 - 5
test/Blocking.hs

@@ -5,7 +5,7 @@ module Blocking (tests) where
 import Distribution.TestSuite
 import Distribution.TestSuite
 import Base (simpleTest)
 import Base (simpleTest)
 import Control.Concurrent(forkIO) 
 import Control.Concurrent(forkIO) 
-import qualified System.IO.Uniform as U
+import System.IO.Uniform.Network
 import qualified System.IO.Uniform.Streamline as S
 import qualified System.IO.Uniform.Streamline as S
 import System.Timeout (timeout)
 import System.Timeout (timeout)
 import Data.ByteString (ByteString)
 import Data.ByteString (ByteString)
@@ -49,7 +49,7 @@ concatLine f = do
 --   the command must not block.
 --   the command must not block.
 successTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
 successTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
 successTimeout txt f = do
 successTimeout txt f = do
-  recv <- U.bindPort 8888
+  recv <- bindPort 8888
   forkIO $ S.withClient (\_ _ -> do
   forkIO $ S.withClient (\_ _ -> do
                             l <- f
                             l <- f
                             S.send l
                             S.send l
@@ -62,7 +62,7 @@ successTimeout txt f = do
                                        then return . Finished $ Pass
                                        then return . Finished $ Pass
                                        else return . Finished . Fail . C8.unpack $ t
                                        else return . Finished . Fail . C8.unpack $ t
                                  ) "127.0.0.1" 8888
                                  ) "127.0.0.1" 8888
-  U.closePort recv
+  closePort recv
   case r' of
   case r' of
     Just r -> return r
     Just r -> return r
     Nothing -> return . Finished . Fail $ "Execution blocked"
     Nothing -> return . Finished . Fail $ "Execution blocked"
@@ -71,7 +71,7 @@ successTimeout txt f = do
 --   Does not care about the result of the command, just wether it blocks.
 --   Does not care about the result of the command, just wether it blocks.
 failTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
 failTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
 failTimeout txt f = do
 failTimeout txt f = do
-  recv <- U.bindPort 8888
+  recv <- bindPort 8888
   forkIO $ S.withClient (\_ _ -> do
   forkIO $ S.withClient (\_ _ -> do
                             f
                             f
                             S.send "\n"
                             S.send "\n"
@@ -82,7 +82,7 @@ failTimeout txt f = do
                                      S.receiveLine
                                      S.receiveLine
                                      return . Finished $ Pass
                                      return . Finished $ Pass
                                  ) "127.0.0.1" 8888
                                  ) "127.0.0.1" 8888
-  U.closePort recv
+  closePort recv
   case r' of
   case r' of
     Just r -> return r
     Just r -> return r
     Nothing -> return . Finished . Fail $ "Execution blocked"
     Nothing -> return . Finished . Fail $ "Execution blocked"

+ 38 - 34
test/Targets.hs

@@ -5,7 +5,11 @@ module Targets (tests) where
 import Distribution.TestSuite
 import Distribution.TestSuite
 import Base (simpleTest)
 import Base (simpleTest)
 import Control.Concurrent(forkIO) 
 import Control.Concurrent(forkIO) 
-import qualified System.IO.Uniform as U
+import System.IO.Uniform
+import System.IO.Uniform.Network
+import System.IO.Uniform.File
+--import System.IO.Uniform.Std
+import System.IO.Uniform.ByteString
 import System.Timeout (timeout)
 import System.Timeout (timeout)
 import qualified Data.ByteString.Char8 as C8
 import qualified Data.ByteString.Char8 as C8
 import Data.ByteString (ByteString)
 import Data.ByteString (ByteString)
@@ -21,23 +25,23 @@ tests = return [
 
 
 testNetwork :: IO Progress
 testNetwork :: IO Progress
 testNetwork = do
 testNetwork = do
-  recv <- U.bindPort 8888
+  recv <- bindPort 8888
   forkIO $ do
   forkIO $ do
-    s <- U.accept recv
-    l <- U.uRead s 100
-    U.uPut s l
-    U.uClose s
+    s <- accept recv
+    l <- uRead s 100
+    uPut s l
+    uClose s
     return ()
     return ()
   r' <- timeout 1000000 $ do
   r' <- timeout 1000000 $ do
-    s <- U.connectToHost "127.0.0.1" 8888
+    s <- connectToHost "127.0.0.1" 8888
     let l = "abcdef\n"
     let l = "abcdef\n"
-    U.uPut s l
-    l' <- U.uRead s 100
-    U.uClose s
+    uPut s l
+    l' <- uRead s 100
+    uClose s
     if l == l'
     if l == l'
       then return . Finished $ Pass
       then return . Finished $ Pass
       else return . Finished . Fail . C8.unpack $ l'
       else return . Finished . Fail . C8.unpack $ l'
-  U.closePort recv
+  closePort recv
   case r' of
   case r' of
     Just r -> return r
     Just r -> return r
     Nothing -> return . Finished . Fail $ "Execution blocked"
     Nothing -> return . Finished . Fail $ "Execution blocked"
@@ -45,39 +49,39 @@ testNetwork = do
 testFile :: IO Progress
 testFile :: IO Progress
 testFile = do
 testFile = do
   let file = "test/testFile"
   let file = "test/testFile"
-  s <- U.openFile file
+  s <- openFile file
   let l = "abcde\n"
   let l = "abcde\n"
-  U.uPut s l
-  U.uClose s
-  s' <- U.openFile file
-  l' <- U.uRead s' 100
-  U.uClose s'
+  uPut s l
+  uClose s
+  s' <- openFile file
+  l' <- uRead s' 100
+  uClose s'
   if l == l'
   if l == l'
     then return . Finished $ Pass
     then return . Finished $ Pass
     else return . Finished . Fail . C8.unpack $ l'
     else return . Finished . Fail . C8.unpack $ l'
 
 
 testTls :: IO Progress
 testTls :: IO Progress
 testTls = do
 testTls = do
-  recv <- U.bindPort 8888
-  let set = U.TlsSettings "test/key.pem" "test/cert.pem" "test/dh.pem"
+  recv <- bindPort 8888
+  let set = TlsSettings "test/key.pem" "test/cert.pem" "test/dh.pem"
   forkIO $ do
   forkIO $ do
-    s' <- U.accept recv
-    s <- U.startTls set s'
-    l <- U.uRead s 100
-    U.uPut s l
-    U.uClose s
+    s' <- accept recv
+    s <- startTls set s'
+    l <- uRead s 100
+    uPut s l
+    uClose s
     return ()
     return ()
   r' <- timeout 1000000 $ do
   r' <- timeout 1000000 $ do
-    s' <- U.connectToHost "127.0.0.1" 8888
-    s <- U.startTls set s'
+    s' <- connectToHost "127.0.0.1" 8888
+    s <- startTls set s'
     let l = "abcdef\n"
     let l = "abcdef\n"
-    U.uPut s l
-    l' <- U.uRead s 100
-    U.uClose s
+    uPut s l
+    l' <- uRead s 100
+    uClose s
     if l == l'
     if l == l'
       then return . Finished $ Pass
       then return . Finished $ Pass
       else return . Finished . Fail . C8.unpack $ l'
       else return . Finished . Fail . C8.unpack $ l'
-  U.closePort recv
+  closePort recv
   case r' of
   case r' of
     Just r -> return r
     Just r -> return r
     Nothing -> return . Finished . Fail $ "Execution blocked"
     Nothing -> return . Finished . Fail $ "Execution blocked"
@@ -85,16 +89,16 @@ testTls = do
 testBS :: IO Progress
 testBS :: IO Progress
 testBS = do
 testBS = do
   let dt = "Some data to test ByteString"
   let dt = "Some data to test ByteString"
-  (len, echo) <- U.withByteStringIO' dt (
+  (len, echo) <- withByteStringIO' dt (
     \io -> let
     \io -> let
       count = countAndEcho io :: Int -> ByteString -> IO Int
       count = countAndEcho io :: Int -> ByteString -> IO Int
-      in U.mapOverInput io 2 count 0
+      in mapOverInput io 2 count 0
     ) :: IO (Int, ByteString)
     ) :: IO (Int, ByteString)
   if dt /= echo || BS.length dt /= len
   if dt /= echo || BS.length dt /= len
     then return . Finished . Fail $ "Failure on ByteStringIO test"
     then return . Finished . Fail $ "Failure on ByteStringIO test"
     else return . Finished $ Pass
     else return . Finished $ Pass
   where
   where
-    countAndEcho :: U.UniformIO io => io -> Int -> ByteString -> IO Int
+    countAndEcho :: UniformIO io => io -> Int -> ByteString -> IO Int
     countAndEcho io initial dt = do
     countAndEcho io initial dt = do
-      U.uPut io dt
+      uPut io dt
       return $ initial + BS.length dt
       return $ initial + BS.length dt

+ 9 - 6
uniform-io.cabal

@@ -10,10 +10,10 @@ name:                uniform-io
 -- PVP summary:      +-+------- breaking API changes
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
 --                   | | | +--- code changes with no API change
-version:    0.2.0.0
+version:    1.0.0.0
 
 
 -- A short (one-line) description of the package.
 -- A short (one-line) description of the package.
-synopsis:   Uniform IO over files, network, watever.
+synopsis:   Uniform IO over files, network, anything.
 
 
 -- A longer description of the package.
 -- A longer description of the package.
 description:
 description:
@@ -70,19 +70,22 @@ source-repository head
 source-repository this
 source-repository this
   type:     git
   type:     git
   location: https://sealgram.com/git/haskell/uniform-io
   location: https://sealgram.com/git/haskell/uniform-io
-  tag:   0.2.0.0
+  tag:   1.0.0.0
 
 
 library
 library
   -- Modules exported by the library.
   -- Modules exported by the library.
   exposed-modules:
   exposed-modules:
       System.IO.Uniform,
       System.IO.Uniform,
+      System.IO.Uniform.Network,
+      System.IO.Uniform.File,
+      System.IO.Uniform.Std,
+      System.IO.Uniform.ByteString,
       System.IO.Uniform.Streamline
       System.IO.Uniform.Streamline
 
 
-  ghc-options: -Wall -fno-warn-unused-do-bind -fwarn-incomplete-patterns
+  ghc-options: -Wall -fno-warn-unused-do-bind -fwarn-incomplete-patterns -fno-warn-orphans
 
 
   -- Modules included in this library but not exported.
   -- Modules included in this library but not exported.
-  other-modules:       
-        System.IO.Uniform.Targets
+  --other-modules:       
 
 
   -- LANGUAGE extensions used by modules in this package.
   -- LANGUAGE extensions used by modules in this package.
   other-extensions:
   other-extensions: