pipes-network-tls-0.2.0: src/Pipes/Network/TCP/TLS.hs
{-# 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.