{-# LANGUAGE CPP #-}
-- |
-- Module      : Network.Socket.ByteString.Lazy
-- Copyright   : (c) Bryan O'Sullivan 2009
-- License     : BSD-style
--
-- Maintainer  : bos@serpentine.com
-- Stability   : experimental
-- Portability : POSIX, GHC
--
-- This module provides access to the BSD /socket/ interface.  For detailed
-- documentation, consult your favorite POSIX socket reference. All functions
-- communicate failures by converting the error number to an
-- 'System.IO.Error.IOError'.
--
-- This module is made to be imported with "Network.Socket" like so:
--
-- > import Network.Socket
-- > import Network.Socket.ByteString.Lazy
-- > import Prelude hiding (getContents)
--
module Network.Socket.ByteString.Lazy (
    -- * Send data to a socket
    send
  , sendAll
    -- * Receive data from a socket
  , getContents
  , recv
  ) where

import           Data.ByteString.Lazy.Internal (ByteString(..), defaultChunkSize)
import           Network.Socket                (ShutdownCmd (..), shutdown)
import           Prelude                       hiding (getContents)
import           System.IO.Unsafe              (unsafeInterleaveIO)
import           System.IO.Error               (catchIOError)

#if defined(mingw32_HOST_OS)
import Network.Socket.ByteString.Lazy.Windows  (send, sendAll)
#else
import Network.Socket.ByteString.Lazy.Posix    (send, sendAll)
#endif

import qualified Data.ByteString               as S
import qualified Network.Socket.ByteString     as N
import           Network.Socket.Imports
import           Network.Socket.Types

-- -----------------------------------------------------------------------------
-- Receiving
-- | Receive data from the socket.  The socket must be in a connected
-- state.  Data is received on demand, in chunks; each chunk will be
-- sized to reflect the amount of data received by individual 'recv'
-- calls.
--
-- All remaining data from the socket is consumed.  When there is no
-- more data to be received, the receiving side of the socket is shut
-- down.  If there is an error and an exception is thrown, the socket
-- is not shut down.
getContents
    :: Socket -- ^ Connected socket
    -> IO ByteString -- ^ Data received
getContents :: Socket -> IO ByteString
getContents Socket
s = IO ByteString
loop
  where
    loop :: IO ByteString
loop = IO ByteString -> IO ByteString
forall a. IO a -> IO a
unsafeInterleaveIO (IO ByteString -> IO ByteString) -> IO ByteString -> IO ByteString
forall a b. (a -> b) -> a -> b
$ do
        ByteString
sbs <- Socket -> Int -> IO ByteString
N.recv Socket
s Int
defaultChunkSize
        if ByteString -> Bool
S.null ByteString
sbs
            then do
              Socket -> ShutdownCmd -> IO ()
shutdown Socket
s ShutdownCmd
ShutdownReceive IO () -> (IOError -> IO ()) -> IO ()
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIOError` IO () -> IOError -> IO ()
forall a b. a -> b -> a
const (() -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())
              ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
Empty
            else ByteString -> ByteString -> ByteString
Chunk ByteString
sbs (ByteString -> ByteString) -> IO ByteString -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString
loop

-- | Receive data from the socket.  The socket must be in a connected
-- state.  This function may return fewer bytes than specified.  If
-- the received data is longer than the specified length, it may be
-- discarded depending on the type of socket.  This function may block
-- until a message arrives.
--
-- If there is no more data to be received, returns an empty 'ByteString'.
recv
    :: Socket -- ^ Connected socket
    -> Int64 -- ^ Maximum number of bytes to receive
    -> IO ByteString -- ^ Data received
recv :: Socket -> Int64 -> IO ByteString
recv Socket
s Int64
nbytes = ByteString -> ByteString
chunk (ByteString -> ByteString) -> IO ByteString -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Socket -> Int -> IO ByteString
N.recv Socket
s (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
nbytes)
  where
    chunk :: ByteString -> ByteString
chunk ByteString
k | ByteString -> Bool
S.null ByteString
k  = ByteString
Empty
            | Bool
otherwise = ByteString -> ByteString -> ByteString
Chunk ByteString
k ByteString
Empty