123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- {-# LANGUAGE ExistentialQuantification #-}
- -- {-# LANGUAGE OverloadedStrings #-}
- -- {-# LANGUAGE ForeignFunctionInterface #-}
- -- {-# LANGUAGE InterruptibleFFI #-}
- -- {-# LANGUAGE EmptyDataDecls #-}
- -- |
- -- Uniform-IO provides a typeclass for uniform access of different types of targets,
- -- and implementations for abstracting standard streams, files and network connections.
- -- This module also provides TLS wraping over other IO targets.
- 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
- -- | 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
|