@@ -26,6 +26,7 @@ module Network.Socket.Options (
2626 , getSockOpt
2727 , setSockOpt
2828 , StructLinger (.. )
29+ , SocketTimeout (.. )
2930 ) where
3031
3132import qualified Text.Read as P
@@ -38,7 +39,9 @@ import Network.Socket.Internal
3839import Network.Socket.Types
3940import Network.Socket.ReadShow
4041
41- -----------------------------------------------------------------------------
42+ #include <sys/time.h>
43+
44+ ----------------------------------------------------------------
4245-- Socket Properties
4346
4447-- | Socket options for use with 'setSocketOption' and 'getSocketOption'.
@@ -55,18 +58,75 @@ data SocketOption = SockOpt
5558#endif
5659 deriving (Eq )
5760
61+ ----------------------------------------------------------------
62+
63+ socketOptionBijection :: Bijection SocketOption String
64+ socketOptionBijection =
65+ [ (UnsupportedSocketOption , " UnsupportedSocketOption" )
66+ , (Debug , " Debug" )
67+ , (ReuseAddr , " ReuseAddr" )
68+ , (SoDomain , " SoDomain" )
69+ , (Type , " Type" )
70+ , (SoProtocol , " SoProtocol" )
71+ , (SoError , " SoError" )
72+ , (DontRoute , " DontRoute" )
73+ , (Broadcast , " Broadcast" )
74+ , (SendBuffer , " SendBuffer" )
75+ , (RecvBuffer , " RecvBuffer" )
76+ , (KeepAlive , " KeepAlive" )
77+ , (OOBInline , " OOBInline" )
78+ , (Linger , " Linger" )
79+ , (ReusePort , " ReusePort" )
80+ , (RecvLowWater , " RecvLowWater" )
81+ , (SendLowWater , " SendLowWater" )
82+ , (RecvTimeOut , " RecvTimeOut" )
83+ , (SendTimeOut , " SendTimeOut" )
84+ , (UseLoopBack , " UseLoopBack" )
85+ , (MaxSegment , " MaxSegment" )
86+ , (NoDelay , " NoDelay" )
87+ , (UserTimeout , " UserTimeout" )
88+ , (Cork , " Cork" )
89+ , (TimeToLive , " TimeToLive" )
90+ , (RecvIPv4TTL , " RecvIPv4TTL" )
91+ , (RecvIPv4TOS , " RecvIPv4TOS" )
92+ , (RecvIPv4PktInfo , " RecvIPv4PktInfo" )
93+ , (IPv6Only , " IPv6Only" )
94+ , (RecvIPv6HopLimit , " RecvIPv6HopLimit" )
95+ , (RecvIPv6TClass , " RecvIPv6TClass" )
96+ , (RecvIPv6PktInfo , " RecvIPv6PktInfo" )
97+ ]
98+
99+ instance Show SocketOption where
100+ showsPrec = bijectiveShow socketOptionBijection def
101+ where
102+ defname = " SockOpt"
103+ unwrap = \ (CustomSockOpt nm) -> nm
104+ def = defShow defname unwrap showIntInt
105+
106+
107+ instance Read SocketOption where
108+ readPrec = bijectiveRead socketOptionBijection def
109+ where
110+ defname = " SockOpt"
111+ def = defRead defname CustomSockOpt readIntInt
112+
113+ ----------------------------------------------------------------
114+
115+ pattern UnsupportedSocketOption :: SocketOption
116+ pattern UnsupportedSocketOption = SockOpt (- 1 ) (- 1 )
117+
58118-- | Does the 'SocketOption' exist on this system?
59119isSupportedSocketOption :: SocketOption -> Bool
60120isSupportedSocketOption opt = opt /= SockOpt (- 1 ) (- 1 )
61121
62- -- | Get the 'SocketType' of an active socket.
63- --
64- -- Since: 3.0.1.0
65- getSocketType :: Socket -> IO SocketType
66- getSocketType s = unpackSocketType <$> getSockOpt s Type
122+ -- | Execute the given action only when the specified socket option is
123+ -- supported. Any return value is ignored.
124+ whenSupported :: SocketOption -> IO a -> IO ()
125+ whenSupported s action
126+ | isSupportedSocketOption s = action >> return ()
127+ | otherwise = return ()
67128
68- pattern UnsupportedSocketOption :: SocketOption
69- pattern UnsupportedSocketOption = SockOpt (- 1 ) (- 1 )
129+ ----------------------------------------------------------------
70130
71131#ifdef SOL_SOCKET
72132-- | SO_ACCEPTCONN, read-only
@@ -192,14 +252,14 @@ pattern SendLowWater = SockOpt (#const SOL_SOCKET) (#const SO_SNDLOWAT)
192252#else
193253pattern SendLowWater = SockOpt (- 1 ) (- 1 )
194254#endif
195- -- | SO_RCVTIMEO: this does not work at this moment.
255+ -- | SO_RCVTIMEO: timeout in microseconds
196256pattern RecvTimeOut :: SocketOption
197257#ifdef SO_RCVTIMEO
198258pattern RecvTimeOut = SockOpt (# const SOL_SOCKET ) (# const SO_RCVTIMEO )
199259#else
200260pattern RecvTimeOut = SockOpt (- 1 ) (- 1 )
201261#endif
202- -- | SO_SNDTIMEO: this does not work at this moment.
262+ -- | SO_SNDTIMEO: timeout in microseconds
203263pattern SendTimeOut :: SocketOption
204264#ifdef SO_SNDTIMEO
205265pattern SendTimeOut = SockOpt (# const SOL_SOCKET ) (# const SO_SNDTIMEO )
@@ -317,41 +377,7 @@ pattern CustomSockOpt xy <- ((\(SockOpt x y) -> (x, y)) -> xy)
317377 where
318378 CustomSockOpt (x, y) = SockOpt x y
319379
320- #if __GLASGOW_HASKELL__ >= 806
321- {-# COMPLETE CustomSockOpt #-}
322- #endif
323- #ifdef SO_LINGER
324- -- | Low level 'SO_LINBER' option value, which can be used with 'setSockOpt'.
325- --
326- data StructLinger = StructLinger {
327- -- | Set the linger option on.
328- sl_onoff :: CInt ,
329-
330- -- | Linger timeout.
331- sl_linger :: CInt
332- }
333- deriving (Eq , Ord , Show )
334-
335- instance Storable StructLinger where
336- sizeOf _ = (# const sizeof(struct linger))
337- alignment _ = alignment (0 :: CInt )
338-
339- peek p = do
340- onoff <- (# peek struct linger, l_onoff) p
341- linger <- (# peek struct linger, l_linger) p
342- return $ StructLinger onoff linger
343-
344- poke p (StructLinger onoff linger) = do
345- (# poke struct linger, l_onoff) p onoff
346- (# poke struct linger, l_linger) p linger
347- #endif
348-
349- -- | Execute the given action only when the specified socket option is
350- -- supported. Any return value is ignored.
351- whenSupported :: SocketOption -> IO a -> IO ()
352- whenSupported s action
353- | isSupportedSocketOption s = action >> return ()
354- | otherwise = return ()
380+ ----------------------------------------------------------------
355381
356382-- | Set a socket option that expects an 'Int' value.
357383setSocketOption :: Socket
@@ -363,6 +389,8 @@ setSocketOption s so@Linger v = do
363389 let arg = if v == 0 then StructLinger 0 0 else StructLinger 1 (fromIntegral v)
364390 setSockOpt s so arg
365391#endif
392+ setSocketOption s so@ RecvTimeOut v = setSockOpt s so $ SocketTimeout $ fromIntegral v
393+ setSocketOption s so@ SendTimeOut v = setSockOpt s so $ SocketTimeout $ fromIntegral v
366394setSocketOption s sa v = setSockOpt s sa (fromIntegral v :: CInt )
367395
368396-- | Set a socket option.
@@ -378,6 +406,8 @@ setSockOpt s (SockOpt level opt) v = do
378406 throwSocketErrorIfMinus1_ " Network.Socket.setSockOpt" $
379407 c_setsockopt fd level opt ptr sz
380408
409+ ----------------------------------------------------------------
410+
381411-- | Get a socket option that gives an 'Int' value.
382412getSocketOption :: Socket
383413 -> SocketOption -- Option Name
@@ -387,6 +417,12 @@ getSocketOption s so@Linger = do
387417 StructLinger onoff linger <- getSockOpt s so
388418 return $ fromIntegral $ if onoff == 0 then 0 else linger
389419#endif
420+ getSocketOption s so@ RecvTimeOut = do
421+ SocketTimeout to <- getSockOpt s so
422+ return $ fromIntegral to
423+ getSocketOption s so@ SendTimeOut = do
424+ SocketTimeout to <- getSockOpt s so
425+ return $ fromIntegral to
390426getSocketOption s so = do
391427 n :: CInt <- getSockOpt s so
392428 return $ fromIntegral n
@@ -404,56 +440,75 @@ getSockOpt s (SockOpt level opt) = do
404440 c_getsockopt fd level opt ptr ptr_sz
405441 peek ptr
406442
443+ ----------------------------------------------------------------
407444
408- socketOptionBijection :: Bijection SocketOption String
409- socketOptionBijection =
410- [ (UnsupportedSocketOption , " UnsupportedSocketOption" )
411- , (Debug , " Debug" )
412- , (ReuseAddr , " ReuseAddr" )
413- , (SoDomain , " SoDomain" )
414- , (Type , " Type" )
415- , (SoProtocol , " SoProtocol" )
416- , (SoError , " SoError" )
417- , (DontRoute , " DontRoute" )
418- , (Broadcast , " Broadcast" )
419- , (SendBuffer , " SendBuffer" )
420- , (RecvBuffer , " RecvBuffer" )
421- , (KeepAlive , " KeepAlive" )
422- , (OOBInline , " OOBInline" )
423- , (Linger , " Linger" )
424- , (ReusePort , " ReusePort" )
425- , (RecvLowWater , " RecvLowWater" )
426- , (SendLowWater , " SendLowWater" )
427- , (RecvTimeOut , " RecvTimeOut" )
428- , (SendTimeOut , " SendTimeOut" )
429- , (UseLoopBack , " UseLoopBack" )
430- , (MaxSegment , " MaxSegment" )
431- , (NoDelay , " NoDelay" )
432- , (UserTimeout , " UserTimeout" )
433- , (Cork , " Cork" )
434- , (TimeToLive , " TimeToLive" )
435- , (RecvIPv4TTL , " RecvIPv4TTL" )
436- , (RecvIPv4TOS , " RecvIPv4TOS" )
437- , (RecvIPv4PktInfo , " RecvIPv4PktInfo" )
438- , (IPv6Only , " IPv6Only" )
439- , (RecvIPv6HopLimit , " RecvIPv6HopLimit" )
440- , (RecvIPv6TClass , " RecvIPv6TClass" )
441- , (RecvIPv6PktInfo , " RecvIPv6PktInfo" )
442- ]
445+ -- | Get the 'SocketType' of an active socket.
446+ --
447+ -- Since: 3.0.1.0
448+ getSocketType :: Socket -> IO SocketType
449+ getSocketType s = unpackSocketType <$> getSockOpt s Type
443450
444- instance Show SocketOption where
445- showsPrec = bijectiveShow socketOptionBijection def
446- where
447- defname = " SockOpt"
448- unwrap = \ (CustomSockOpt nm) -> nm
449- def = defShow defname unwrap showIntInt
451+ ----------------------------------------------------------------
450452
453+ #if __GLASGOW_HASKELL__ >= 806
454+ {-# COMPLETE CustomSockOpt #-}
455+ #endif
456+ #ifdef SO_LINGER
457+ -- | Low level 'SO_LINBER' option value, which can be used with 'setSockOpt'.
458+ --
459+ data StructLinger = StructLinger {
460+ -- | Set the linger option on.
461+ sl_onoff :: CInt ,
451462
452- instance Read SocketOption where
453- readPrec = bijectiveRead socketOptionBijection def
454- where
455- defname = " SockOpt"
456- def = defRead defname CustomSockOpt readIntInt
463+ -- | Linger timeout.
464+ sl_linger :: CInt
465+ }
466+ deriving (Eq , Ord , Show )
467+
468+ instance Storable StructLinger where
469+ sizeOf _ = (# const sizeof(struct linger))
470+ alignment _ = alignment (0 :: CInt )
471+
472+ peek p = do
473+ onoff <- (# peek struct linger, l_onoff) p
474+ linger <- (# peek struct linger, l_linger) p
475+ return $ StructLinger onoff linger
476+
477+ poke p (StructLinger onoff linger) = do
478+ (# poke struct linger, l_onoff) p onoff
479+ (# poke struct linger, l_linger) p linger
480+ #endif
481+
482+ ----------------------------------------------------------------
483+
484+ -- | Timeout in microseconds.
485+ -- This will be converted into struct timeval on Unix and
486+ -- DWORD (as milliseconds) on Windows.
487+ newtype SocketTimeout = SocketTimeout Word32 deriving (Eq , Ord , Show )
488+
489+ #if defined(mingw32_HOST_OS)
490+ instance Storable SocketTimeout where
491+ sizeOf (SocketTimeout to) = sizeOf to -- DWORD as milliseconds
492+ alignment _ = 0
493+ peek ptr = do
494+ to <- peek (castPtr ptr)
495+ return $ SocketTimeout (to * 1000 )
496+ poke ptr (SocketTimeout to) = poke (castPtr ptr) (to `div` 1000 )
497+ #else
498+ instance Storable SocketTimeout where
499+ sizeOf _ = (# size struct timeval)
500+ alignment _ = (# const offsetof(struct {char x__; struct timeval (y__); }, y__))
501+ peek ptr = do
502+ sec <- (# peek struct timeval, tv_sec) ptr
503+ usec <- (# peek struct timeval, tv_usec) ptr
504+ return $ SocketTimeout (sec * 1000000 + usec)
505+ poke ptr (SocketTimeout to) = do
506+ let (sec, usec) = to `divMod` 1000000
507+ (# poke struct timeval, tv_sec) ptr sec
508+ (# poke struct timeval, tv_usec) ptr usec
509+ #endif
510+
511+ ----------------------------------------------------------------
457512
458513foreign import CALLCONV unsafe " getsockopt"
459514 c_getsockopt :: CInt -> CInt -> CInt -> Ptr a -> Ptr CInt -> IO CInt
0 commit comments