socket-0.8.3.0: src/System/Socket/Internal/SocketOption.hsc
--------------------------------------------------------------------------------
-- |
-- Module : System.Socket.Internal.SocketOption
-- Copyright : (c) Lars Petersen 2016
-- License : MIT
--
-- Maintainer : info@lars-petersen.net
-- Stability : experimental
--------------------------------------------------------------------------------
module System.Socket.Internal.SocketOption (
SocketOption (..)
, unsafeGetSocketOption
, unsafeSetSocketOption
, Error (..)
, ReuseAddress (..)
, KeepAlive(..)
) where
import Control.Concurrent.MVar
import Control.Exception
import Control.Monad
import Control.Applicative ((<$>))
import Foreign.Ptr
import Foreign.Storable
import Foreign.C.Types
import Foreign.Marshal.Alloc
import System.Socket.Internal.Socket
import System.Socket.Internal.Platform
import System.Socket.Internal.Exception
#include "hs_socket.h"
-- | `SocketOption`s allow to read and write certain properties of a socket.
--
-- - Each option shall have a corresponding data type that models the data
-- associated with the socket option.
-- - Use `System.Socket.Unsafe.unsafeGetSocketOption` and
-- `System.Socket.Unsafe.unsafeSetSocketOption` in order to implement custom socket
-- options.
class SocketOption o where
-- | Get a specific `SocketOption`.
--
-- - This operation throws `SocketException`s. Consult @man getsockopt@ for
-- details and specific errors.
getSocketOption :: Socket f t p -> IO o
-- | Set a specific `SocketOption`.
--
-- - This operation throws `SocketException`s. Consult @man setsockopt@ for
-- details and specific errors.
setSocketOption :: Socket f t p -> o -> IO ()
-- | Reports the last error that occured on the socket.
--
-- - Also known as @SO_ERROR@.
-- - The operation `setSocketOption` always throws `eInvalid` for this option.
-- - Use with care in the presence of concurrency!
data Error
= Error SocketException
deriving (Eq, Ord, Show)
instance SocketOption Error where
getSocketOption s =
Error . SocketException Control.Applicative.<$> unsafeGetSocketOption s (#const SOL_SOCKET) (#const SO_ERROR)
setSocketOption _ _ = throwIO eInvalid
-- | Allows or disallows the reuse of a local address in a `System.Socket.bind` call.
--
-- - Also known as @SO_REUSEADDR@.
-- - This is particularly useful when experiencing `eAddressInUse` exceptions.
data ReuseAddress
= ReuseAddress Bool
deriving (Eq, Ord, Show)
instance SocketOption ReuseAddress where
getSocketOption s =
ReuseAddress . ((/=0) :: CInt -> Bool) <$> unsafeGetSocketOption s (#const SOL_SOCKET) (#const SO_REUSEADDR)
setSocketOption s (ReuseAddress o) =
unsafeSetSocketOption s (#const SOL_SOCKET) (#const SO_REUSEADDR) (if o then 1 else 0 :: CInt)
-- | When enabled the protocol checks in a protocol-specific manner
-- if the other end is still alive.
--
-- - Also known as @SO_KEEPALIVE@.
data KeepAlive
= KeepAlive Bool
deriving (Eq, Ord, Show)
instance SocketOption KeepAlive where
getSocketOption s =
KeepAlive . ((/=0) :: CInt -> Bool) <$> unsafeGetSocketOption s (#const SOL_SOCKET) (#const SO_KEEPALIVE)
setSocketOption s (KeepAlive o) =
unsafeSetSocketOption s (#const SOL_SOCKET) (#const SO_KEEPALIVE) (if o then 1 else 0 :: CInt)
-------------------------------------------------------------------------------
-- Unsafe helpers
-------------------------------------------------------------------------------
unsafeSetSocketOption :: Storable a => Socket f t p -> CInt -> CInt -> a -> IO ()
unsafeSetSocketOption (Socket mfd) level name value =
withMVar mfd $ \fd-> alloca $ \vPtr-> alloca $ \errPtr-> do
poke vPtr value
i <- c_setsockopt fd level name vPtr (fromIntegral $ sizeOf value) errPtr
when (i < 0) (SocketException <$> peek errPtr >>= throwIO)
unsafeGetSocketOption :: Storable a => Socket f t p -> CInt -> CInt -> IO a
unsafeGetSocketOption (Socket mfd) level name =
withMVar mfd $ \fd-> alloca $ \vPtr-> alloca $ \lPtr-> alloca $ \errPtr-> do
u <- return undefined
poke lPtr (fromIntegral $ sizeOf u)
i <- c_getsockopt fd level name vPtr (lPtr :: Ptr CInt) errPtr
when (i < 0) (SocketException <$> peek errPtr >>= throwIO)
x <- peek vPtr
return (x `asTypeOf` u)