-
Notifications
You must be signed in to change notification settings - Fork 190
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,13 +2,39 @@ | |
|
||
#include <sys/uio.h> | ||
|
||
-- | | ||
-- Module : Network.Socket.ByteString.Lazy | ||
-- Copyright : (c) Bryan O'Sullivan 2009 | ||
-- License : BSD-style | ||
-- | ||
-- Maintainer : [email protected] | ||
-- Stability : experimental | ||
-- Portability : POSIX, GHC | ||
-- | ||
-- A module for efficiently transmitting data over sockets. For | ||
-- detailed documentation consult your favorite POSIX socket | ||
-- reference. All functions communicate failures by converting the | ||
-- error number to an 'System.IO.IOError'. | ||
-- | ||
-- This module is intended to be imported together with | ||
-- 'Network.Socket' like so: | ||
-- | ||
-- > import Network.Socket hiding (send, sendTo, recv, recvFrom) | ||
-- > import Network.Socket.ByteString.Lazy | ||
-- > import Prelude hiding (getContents) | ||
-- | ||
-- Alternatively, you can import it qualified. | ||
-- | ||
-- > import qualified Network.Socket.ByteString.Lazy as S | ||
module Network.Socket.ByteString.Lazy | ||
( | ||
getContents | ||
-- * Send data on a socket | ||
sendAll | ||
, send | ||
, sendAll | ||
-- * Receive data on a socket | ||
, recv | ||
, recv_ | ||
, getContents | ||
) where | ||
|
||
import Control.Monad (liftM) | ||
|
@@ -23,14 +49,15 @@ import Foreign.Ptr (Ptr, castPtr, plusPtr) | |
import Foreign.Storable (Storable(..)) | ||
import qualified Network.Socket.ByteString as N | ||
import Network.Socket.ByteString.Internal | ||
import Network.Socket (Socket(..)) | ||
import Network.Socket (Socket(..), ShutdownCmd(..), shutdown) | ||
import System.IO.Unsafe (unsafeInterleaveIO) | ||
import Prelude hiding (getContents) | ||
import System.Posix.Types (CSsize) | ||
import GHC.Conc (threadWaitRead, threadWaitWrite) | ||
|
||
-- | Needed to support the POSIX writev system call. | ||
data IOVec = IOVec { iovBase :: Ptr CChar | ||
, iovLen :: CSize } | ||
, iovLen :: CSize } | ||
|
||
instance Storable IOVec where | ||
sizeOf _ = (#const sizeof(struct iovec)) | ||
|
@@ -45,6 +72,13 @@ instance Storable IOVec where | |
(#poke struct iovec, iov_base) p (iovBase iov) | ||
(#poke struct iovec, iov_len) p (iovLen iov) | ||
|
||
-- | Send a 'ByteString' using a single system call. | ||
-- | ||
-- Because a lazily generated 'ByteString' may be arbitrarily long, | ||
-- this function caps the amount it will attempt to send at 4MB. This | ||
-- number is large (so it should not penalize performance on fast | ||
-- networks), but not outrageously so (to avoid demanding lazily | ||
-- computed data unnecessarily early). | ||
send :: Socket -> ByteString -> IO Int64 | ||
send (MkSocket fd _ _ _ _) s = do | ||
let cs = L.toChunks s | ||
|
@@ -65,31 +99,52 @@ send (MkSocket fd _ _ _ _) s = do | |
(k + fromIntegral len) (niovs + 1) | ||
| otherwise = f niovs | ||
loop _ _ _ niovs = f niovs | ||
-- Limit the amount of data that we'll try to transmit with a | ||
-- single system call. | ||
sendLimit = 4194304 | ||
|
||
foreign import ccall unsafe "writev" | ||
c_writev :: CInt -> Ptr IOVec -> CInt -> IO CSsize | ||
|
||
-- | Send the entire contents of a string, possibly using multiple | ||
-- 'send' system calls to do so. | ||
sendAll :: Socket -> ByteString -> IO () | ||
sendAll sock bs = do | ||
sent <- send sock bs | ||
if sent < L.length bs | ||
then sendAll sock (L.drop sent bs) | ||
else return () | ||
|
||
-- | Lazily receive 'ByteString' data, in chunks. Chunks are received | ||
-- on demand; each chunk will be sized to reflect the amount of data | ||
-- received by individual 'recv_' calls. | ||
-- | ||
-- All remaining data from the socket data is consumed. The receiving | ||
-- side of the socket is shut down when there is no more data to be | ||
-- received. This does not occur if an exception is thrown. | ||
getContents :: Socket -> IO ByteString | ||
getContents sock@(MkSocket fd _ _ _ _) = loop | ||
where loop = unsafeInterleaveIO $ do | ||
s <- N.recv_ sock defaultChunkSize | ||
if S.null s | ||
then return Empty | ||
then shutdown sock ShutdownReceive >> return Empty | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
eborden
Collaborator
|
||
else Chunk s `liftM` loop | ||
|
||
-- | Receive a message. The socket must be in a connected state so | ||
-- that the intended recipient is known. Note that the length of the | ||
-- received data can be smaller than specified maximum length. If the | ||
-- message is longer than the specified length it may be discarded | ||
-- depending on the type of socket. May block until a message arrives. | ||
-- | ||
-- When the end of the input stream is reached, returns an empty | ||
-- 'ByteString'. | ||
recv_ :: Socket -> Int64 -> IO ByteString | ||
recv_ sock nbytes = chunk `liftM` N.recv_ sock (fromIntegral nbytes) | ||
where chunk k | S.null k = Empty | ||
| otherwise = Chunk k Empty | ||
|
||
-- | Receive a message from another socket. Similar to 'recv_', but | ||
-- throws an EOF exception at end of input. | ||
recv :: Socket -> Int64 -> IO ByteString | ||
recv sock nbytes = chunk `liftM` N.recv sock (fromIntegral nbytes) | ||
where chunk k = Chunk k Empty |
1 comment
on commit 0d0b990
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have bravery to remove unknown code.
@bos, @eborden, @kazu-yamamoto This
shutdown
call is causing problems in MacOS tests. Does anyone know why it was added? Adding ashutdown
afterrecv()
returns EOF looks redundant.I can make the MacOS
getContents
tests pass, by either removing thisshutdown
, or ignoring some or all IO exceptions that it may raise.