{-# LANGUAGE RankNTypes #-} -- | This module exports functions that allow you to use TLS-secured -- TCP connections in a streaming fashion. -- -- You are encouraged to use this module together with "Network.Simple.TCP.TLS" -- as follows: -- -- @ -- import qualified "Network.Simple.TCP.TLS" as TLS -- import qualified "Pipes.Network.TCP.TLS" as TLS -- @ -- -- This module /does not/ export facilities that would allow you to establish -- new connections within a pipeline. If you need to do so, then you should use -- "Pipes.Network.TCP.TLS.Safe" instead, which exports a similar API to the one -- exported by this module. Don't be confused by the word “safe” in that module; -- this module is equally safe to use as long as you don't try to acquire new -- resources within the pipeline. module Pipes.Network.TCP.TLS ( -- * Receiving -- $receiving fromContext , fromContextTimeout -- * Sending -- $sending , toContext , toContextTimeout ) where import Pipes import qualified Data.ByteString as B import Foreign.C.Error (errnoToIOError, eTIMEDOUT) import Network.Simple.TCP.TLS import System.Timeout (timeout) -------------------------------------------------------------------------------- -- $client-side -- -- Here's how you could run a simple TLS-secured TCP client: -- -- @ -- import qualified "Pipes.Network.TCP.TLS" as TLS -- -- \ settings <- 'getDefaultClientSettings' -- 'connect' settings \"www.example.org\" \"443\" $ \(tlsCtx, remoteAddr) -> do -- putStrLn $ \"Secure connection established to \" ++ show remoteAddr -- -- now you may use tlsCtx as you please within this scope, possibly with -- -- the 'fromContext' or 'toContext' proxies explained below. -- @ -------------------------------------------------------------------------------- -- $server-side -- -- Here's how you could run a simple TLS-secured TCP server that handles in -- different threads each incoming connection to port @4433@ at hostname -- @example.org@. You will need a X509 certificate and a private key appropiate -- to be used at that hostname. -- -- @ -- import qualified "Pipes.Network.TCP.TLS" as TLS -- import "Network.TLS.Extra" (fileReadCertificate, fileReadPrivateKey) -- -- \ cert <- 'Network.TLS.Extra.fileReadCertificate' \"~/example.org.crt\" -- pkey <- 'Network.TLS.Extra.fileReadPrivateKey' \"~/example.org.key\" -- let cred = 'Credential' cert pkey [] -- settings = 'makeServerSettings' cred Nothing -- -- \ 'serve' settings ('Host' \"example.org\") \"4433\" $ \(tlsCtx, remoteAddr) -> do -- putStrLn $ \"Secure connection established from \" ++ show remoteAddr -- -- now you may use tlsCtx as you please within this scope, possibly with -- -- the 'fromContext' or 'toContext' proxies explained below. -- @ -- -- If you need more control on the way your server runs, then you can use more -- advanced functions such as 'listen', 'accept' or 'acceptFork'. -------------------------------------------------------------------------------- -- $sending -- -- The following pipes allow you to send bytes to the remote end over a -- TLS-secured TCP connection. -- -- Besides the pipes below, you might want to use "Network.Simple.TCP.TLS"'s -- 'Network.Simple.TCP.TLS.send', which happens to be an 'Effect'': -- -- @ -- 'TLS.send' :: 'MonadIO' m => 'TLS.Context' -> 'B.ByteString' -> 'Effect'' m () -- @ -- | Encrypts and sends to the remote end each 'B.ByteString' received from -- upstream. toContext :: MonadIO m => Context -- ^Established TLS connection context. -> Consumer' B.ByteString m r toContext ctx = for cat (\a -> send ctx a) {-# INLINABLE toContext #-} -- | Like 'toContext', except with the first 'Int' argument you can specify -- the maximum time that each interaction with the remote end can take. If such -- time elapses before the interaction finishes, then an 'IOError' exception is -- thrown. The time is specified in microseconds (10e6). toContextTimeout :: MonadIO m => Int -- ^Timeout in microseconds (1/10^6 seconds). -> Context -- ^Established TLS connection context. -> Consumer' B.ByteString m r toContextTimeout wait ctx = for cat $ \a -> do mu <- liftIO $ timeout wait (send ctx a) case mu of Just () -> return () Nothing -> liftIO $ ioError $ errnoToIOError "Pipes.Network.TCP.TLS.toContextTimeout" eTIMEDOUT Nothing Nothing {-# INLINABLE toContextTimeout #-} -------------------------------------------------------------------------------- -- $receiving -- -- The following pipes allow you to receive bytes from the remote end over a -- TLS-secured TCP connection. -- -- Besides the pipes below, you might want to use "Network.Simple.TCP.TLS"'s -- 'TLS.recv', which happens to be an 'Effect'': -- -- @ -- 'TLS.recv' :: 'MonadIO' m => 'TLS.Context' -> 'Effect'' m ('Maybe' 'B.ByteString') -- @ -- | Receives decrypted bytes from the remote end and sends them downstream. -- -- The number of bytes received at once is always in the interval -- /[1 .. 16384]/. -- -- The TLS connection is automatically renegotiated if a /ClientHello/ message -- is received. -- -- This 'Producer'' returns if the remote peer closes its side of the connection -- or EOF is received. fromContext :: MonadIO m => Context -- ^Established TLS connection context. -> Producer' B.ByteString m () fromContext ctx = loop where loop = do mbs <- recv ctx case mbs of Nothing -> return () Just bs -> yield bs >> loop {-# INLINABLE fromContext #-} -- | Like 'fromContext', except with the first 'Int' argument you can specify -- the maximum time that each interaction with the remote end can take. If such -- time elapses before the interaction finishes, then an 'IOError' exception is -- thrown. The time is specified in microseconds (10e6). fromContextTimeout :: MonadIO m => Int -- ^Timeout in microseconds (1/10^6 seconds). -> Context -- ^Established TLS connection context. -> Producer' B.ByteString m () fromContextTimeout wait ctx = loop where loop = do mmbs <- liftIO $ timeout wait (recv ctx) case mmbs of Just (Just bs) -> yield bs >> loop Just Nothing -> return () Nothing -> liftIO $ ioError $ errnoToIOError "Pipes.Network.TCP.TLS.fromContextTimeout" eTIMEDOUT Nothing Nothing {-# INLINABLE fromContextTimeout #-} -------------------------------------------------------------------------------- -- $exports -- The entire "Network.Simple.TCP.TLS" module is exported.