network-2.4.1.2: Network/Socket/Internal.hsc
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : Network.Socket.Internal
-- Copyright : (c) The University of Glasgow 2001
-- License : BSD-style (see the file libraries/network/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : portable
--
-- A module containing semi-public 'Network.Socket' internals.
-- Modules which extend the 'Network.Socket' module will need to use
-- this module while ideally most users will be able to make do with
-- the public interface.
--
-----------------------------------------------------------------------------
#include "HsNet.h"
module Network.Socket.Internal
(
-- * Socket addresses
HostAddress
#if defined(IPV6_SOCKET_SUPPORT)
, HostAddress6
, FlowInfo
, ScopeID
#endif
, PortNumber(..)
, SockAddr(..)
, peekSockAddr
, pokeSockAddr
, sizeOfSockAddr
, sizeOfSockAddrByFamily
, withSockAddr
, withNewSockAddr
-- * Protocol families
, Family(..)
-- * Socket error functions
#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)
, c_getLastError
#endif
, throwSocketError
, throwSocketErrorCode
-- * Guards for socket operations that may fail
, throwSocketErrorIfMinus1_
, throwSocketErrorIfMinus1Retry
, throwSocketErrorIfMinus1RetryMayBlock
-- ** Guards that wait and retry if the operation would block
-- | These guards are based on 'throwSocketErrorIfMinus1RetryMayBlock'.
-- They wait for socket readiness if the action fails with @EWOULDBLOCK@
-- or similar.
, throwSocketErrorWaitRead
, throwSocketErrorWaitWrite
-- * Initialization
, withSocketsDo
-- * Low-level helpers
, zeroMemory
) where
import Data.Bits ( (.|.), shiftL, shiftR )
import Data.Word ( Word8, Word16, Word32 )
import Data.Typeable (Typeable)
import Foreign.C.Error (throwErrno, throwErrnoIfMinus1Retry,
throwErrnoIfMinus1RetryMayBlock, throwErrnoIfMinus1_,
Errno(..), errnoToIOError)
import Foreign.C.String ( castCharToCChar, peekCString )
#if __GLASGOW_HASKELL__ >= 703
import Foreign.C.Types ( CInt(..), CSize(..) )
#else
import Foreign.C.Types ( CInt, CSize )
#endif
import Foreign.Marshal.Alloc ( allocaBytes )
import Foreign.Marshal.Array ( pokeArray, pokeArray0 )
import Foreign.Ptr ( Ptr, castPtr, plusPtr )
import Foreign.Storable ( Storable(..) )
import GHC.Conc (threadWaitRead, threadWaitWrite)
#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)
import Control.Exception ( finally )
# if __GLASGOW_HASKELL__ >= 707
import GHC.IO.Exception ( IOErrorType(..) )
# else
import GHC.IOBase ( IOErrorType(..) )
# endif
import Foreign.C.Types ( CChar )
import System.IO.Error ( ioeSetErrorString, mkIOError )
#endif
import Network.Socket.Types
-- ---------------------------------------------------------------------
-- Guards for socket operations that may fail
-- | Throw an 'IOError' corresponding to the current socket error.
throwSocketError :: String -- ^ textual description of the error location
-> IO a
-- | Like 'throwSocketError', but the error code is supplied as an argument.
--
-- On Windows, do not use errno. Use a system error code instead.
throwSocketErrorCode :: String -> CInt -> IO a
-- | Throw an 'IOError' corresponding to the current socket error if
-- the IO action returns a result of @-1@. Discards the result of the
-- IO action after error handling.
throwSocketErrorIfMinus1_
:: (Eq a, Num a)
=> String -- ^ textual description of the location
-> IO a -- ^ the 'IO' operation to be executed
-> IO ()
{-# SPECIALIZE throwSocketErrorIfMinus1_ :: String -> IO CInt -> IO () #-}
-- | Throw an 'IOError' corresponding to the current socket error if
-- the IO action returns a result of @-1@, but retries in case of an
-- interrupted operation.
throwSocketErrorIfMinus1Retry
:: (Eq a, Num a)
=> String -- ^ textual description of the location
-> IO a -- ^ the 'IO' operation to be executed
-> IO a
{-# SPECIALIZE throwSocketErrorIfMinus1Retry :: String -> IO CInt -> IO CInt #-}
-- | Throw an 'IOError' corresponding to the current socket error if
-- the IO action returns a result of @-1@, but retries in case of an
-- interrupted operation. Checks for operations that would block and
-- executes an alternative action before retrying in that case.
throwSocketErrorIfMinus1RetryMayBlock
:: (Eq a, Num a)
=> String -- ^ textual description of the location
-> IO b -- ^ action to execute before retrying if an
-- immediate retry would block
-> IO a -- ^ the 'IO' operation to be executed
-> IO a
{-# SPECIALIZE throwSocketErrorIfMinus1RetryMayBlock
:: String -> IO b -> IO CInt -> IO CInt #-}
#if (!defined(HAVE_WINSOCK2_H) || defined(cygwin32_HOST_OS))
throwSocketErrorIfMinus1RetryMayBlock name on_block act =
throwErrnoIfMinus1RetryMayBlock name act on_block
throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry
throwSocketErrorIfMinus1_ = throwErrnoIfMinus1_
throwSocketError = throwErrno
throwSocketErrorCode loc errno =
ioError (errnoToIOError loc (Errno errno) Nothing Nothing)
#else
throwSocketErrorIfMinus1RetryMayBlock name _ act
= throwSocketErrorIfMinus1Retry name act
throwSocketErrorIfMinus1_ name act = do
throwSocketErrorIfMinus1Retry name act
return ()
# if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)
throwSocketErrorIfMinus1Retry name act = do
r <- act
if (r == -1)
then do
rc <- c_getLastError
case rc of
#{const WSANOTINITIALISED} -> do
withSocketsDo (return ())
r <- act
if (r == -1)
then throwSocketError name
else return r
_ -> throwSocketError name
else return r
throwSocketErrorCode name rc = do
pstr <- c_getWSError rc
str <- peekCString pstr
ioError (ioeSetErrorString (mkIOError OtherError name Nothing Nothing) str)
throwSocketError name =
c_getLastError >>= throwSocketErrorCode name
foreign import CALLCONV unsafe "WSAGetLastError"
c_getLastError :: IO CInt
foreign import ccall unsafe "getWSErrorDescr"
c_getWSError :: CInt -> IO (Ptr CChar)
# else
throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry
throwSocketError = throwErrno
throwSocketErrorCode loc errno =
ioError (errnoToIOError loc (Errno errno) Nothing Nothing)
# endif
#endif
-- | Like 'throwSocketErrorIfMinus1Retry', but if the action fails with
-- @EWOULDBLOCK@ or similar, wait for the socket to be read-ready,
-- and try again.
throwSocketErrorWaitRead :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
throwSocketErrorWaitRead sock name io =
throwSocketErrorIfMinus1RetryMayBlock name
(threadWaitRead $ fromIntegral $ sockFd sock)
io
-- | Like 'throwSocketErrorIfMinus1Retry', but if the action fails with
-- @EWOULDBLOCK@ or similar, wait for the socket to be write-ready,
-- and try again.
throwSocketErrorWaitWrite :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
throwSocketErrorWaitWrite sock name io =
throwSocketErrorIfMinus1RetryMayBlock name
(threadWaitWrite $ fromIntegral $ sockFd sock)
io
-- ---------------------------------------------------------------------------
-- WinSock support
{-| On Windows operating systems, the networking subsystem has to be
initialised using 'withSocketsDo' before any networking operations can
be used. eg.
> main = withSocketsDo $ do {...}
Although this is only strictly necessary on Windows platforms, it is
harmless on other platforms, so for portability it is good practice to
use it all the time.
-}
withSocketsDo :: IO a -> IO a
#if !defined(WITH_WINSOCK)
withSocketsDo x = x
#else
withSocketsDo act = do
x <- initWinSock
if x /= 0
then ioError (userError "Failed to initialise WinSock")
else act `finally` shutdownWinSock
foreign import ccall unsafe "initWinSock" initWinSock :: IO Int
foreign import ccall unsafe "shutdownWinSock" shutdownWinSock :: IO ()
#endif