Skip to content

Commit 28dc278

Browse files
committed
implementing annotateIOException
1 parent 7a177c1 commit 28dc278

File tree

3 files changed

+93
-24
lines changed

3 files changed

+93
-24
lines changed

Network/Socket/Internal.hs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module Network.Socket.Internal
3737
#if defined(mingw32_HOST_OS)
3838
, throwSocketErrorIfMinus1ButRetry
3939
#endif
40+
, annotateIOException
4041
-- ** Guards that wait and retry if the operation would block
4142
-- | These guards are based on 'throwSocketErrorIfMinus1RetryMayBlock'.
4243
-- They wait for socket readiness if the action fails with @EWOULDBLOCK@
@@ -78,6 +79,65 @@ import Network.Socket.Cbits
7879
import Network.Socket.Imports
7980
import Network.Socket.Types
8081

82+
import qualified Foreign.C.Error as C
83+
import GHC.IO.Exception (IOException (..))
84+
import System.IO.Error (modifyIOError)
85+
86+
annotateIOException :: IO a -> String -> IO a
87+
annotateIOException io anno = modifyIOError f io
88+
where
89+
f ioe = ioe { ioe_description = ioe_description ioe ++ " " ++ errname ++ anno }
90+
where
91+
errname = case ioe_errno ioe of
92+
Nothing -> ""
93+
Just n -> "[" ++ showErrno n ++ "] "
94+
95+
showErrno :: CInt -> String
96+
showErrno n = case lookup (Errno n) errnoNames of
97+
Nothing -> show n
98+
Just name -> name
99+
100+
errnoNames :: [(Errno, String)]
101+
errnoNames = [
102+
(C.eACCES, "EACCES")
103+
, (C.eADDRINUSE, "EADDRINUSE")
104+
, (C.eADDRNOTAVAIL, "EADDRNOTAVAIL")
105+
, (C.eAFNOSUPPORT, "EAFNOSUPPORT")
106+
, (C.eAGAIN, "EAGAIN")
107+
, (C.eBADF, "EBADF")
108+
, (C.eCONNABORTED, "ECONNABORTED")
109+
, (C.eCONNRESET, "ECONNRESET")
110+
, (C.eDESTADDRREQ, "EDESTADDRREQ")
111+
, (C.eEXIST, "EEXIST")
112+
, (C.eFAULT, "EFAULT")
113+
, (C.eINTR, "EINTR")
114+
, (C.eINVAL, "EINVAL")
115+
, (C.eIO, "EIO")
116+
, (C.eISCONN, "EISCONN")
117+
, (C.eISDIR, "EISDIR")
118+
, (C.eLOOP, "ELOOP")
119+
, (C.eMFILE, "EMFILE")
120+
, (C.eMSGSIZE, "EMSGSIZE")
121+
, (C.eNAMETOOLONG, "ENAMETOOLONG")
122+
, (C.eNETDOWN, "ENETDOWN")
123+
, (C.eNETUNREACH, "ENETUNREACH")
124+
, (C.eMFILE, "EMFILE")
125+
, (C.eNFILE, "ENFILE")
126+
, (C.eNOBUFS, "ENOBUFS")
127+
, (C.eNOENT, "ENOENT")
128+
, (C.eNOMEM, "ENOMEM")
129+
, (C.eNOTCONN, "ENOTCONN")
130+
, (C.eNOTDIR, "ENOTDIR")
131+
, (C.eNOTSOCK, "ENOTSOCK")
132+
, (C.eOPNOTSUPP, "EOPNOTSUPP")
133+
, (C.ePIPE, "EPIPE")
134+
, (C.ePROTONOSUPPORT, "EPROTONOSUPPORT")
135+
, (C.ePROTOTYPE, "EPROTOTYPE")
136+
, (C.eROFS, "EROFS")
137+
, (C.eTIMEDOUT, "ETIMEDOUT")
138+
, (C.eWOULDBLOCK, "EWOULDBLOCK")
139+
]
140+
81141
-- ---------------------------------------------------------------------
82142
-- Guards for socket operations that may fail
83143

Network/Socket/SockAddr.hs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import System.IO.Error (isAlreadyInUseError, isDoesNotExistError)
1919
import qualified Network.Socket.Buffer as G
2020
import Network.Socket.Flag
2121
import Network.Socket.Imports
22+
import Network.Socket.Internal
2223
import qualified Network.Socket.Name as G
2324
import qualified Network.Socket.Syscall as G
2425
#if !defined(mingw32_HOST_OS)
@@ -30,23 +31,26 @@ import Network.Socket.Types
3031

3132
-- | Getting peer's 'SockAddr'.
3233
getPeerName :: Socket -> IO SockAddr
33-
getPeerName = G.getPeerName
34+
getPeerName s = G.getPeerName s `annotateIOException` show s
3435

3536
-- | Getting my 'SockAddr'.
3637
getSocketName :: Socket -> IO SockAddr
37-
getSocketName = G.getSocketName
38+
getSocketName s = G.getSocketName s `annotateIOException` show s
3839

3940
-- | Connect to a remote socket at address.
4041
connect :: Socket -> SockAddr -> IO ()
41-
connect = G.connect
42+
connect s sa = G.connect s sa `annotateIOException` (show s ++ " " ++ show sa)
4243

4344
-- | Bind the socket to an address. The socket must not already be
4445
-- bound. The 'Family' passed to @bind@ must be the
4546
-- same as that passed to 'socket'. If the special port number
4647
-- 'defaultPort' is passed then the system assigns the next available
4748
-- use port.
4849
bind :: Socket -> SockAddr -> IO ()
49-
bind s sa = case sa of
50+
bind s sa = bind' s sa `annotateIOException` (show s ++ " " ++ show sa)
51+
52+
bind' :: Socket -> SockAddr -> IO ()
53+
bind' s sa = case sa of
5054
SockAddrUnix p -> do
5155
-- gracefully handle the fact that UNIX systems don't clean up closed UNIX
5256
-- domain sockets, inspired by https://stackoverflow.com/a/13719866
@@ -73,14 +77,14 @@ bind s sa = case sa of
7377
-- to the socket on the other end of the connection.
7478
-- On Unix, FD_CLOEXEC is set to the new 'Socket'.
7579
accept :: Socket -> IO (Socket, SockAddr)
76-
accept = G.accept
80+
accept s = G.accept s `annotateIOException` show s
7781

7882
-- | Send data to the socket. The recipient can be specified
7983
-- explicitly, so the socket need not be in a connected state.
8084
-- Returns the number of bytes sent. Applications are responsible for
8185
-- ensuring that all data has been sent.
8286
sendBufTo :: Socket -> Ptr a -> Int -> SockAddr -> IO Int
83-
sendBufTo = G.sendBufTo
87+
sendBufTo s ptr len sa = G.sendBufTo s ptr len sa `annotateIOException` (show s ++ " " ++ show sa)
8488

8589
-- | Receive data from the socket, writing it into buffer instead of
8690
-- creating a new string. The socket need not be in a connected
@@ -95,7 +99,7 @@ sendBufTo = G.sendBufTo
9599
-- NOTE: blocking on Windows unless you compile with -threaded (see
96100
-- GHC ticket #1129)
97101
recvBufFrom :: Socket -> Ptr a -> Int -> IO (Int, SockAddr)
98-
recvBufFrom = G.recvBufFrom
102+
recvBufFrom s ptr len = G.recvBufFrom s ptr len `annotateIOException` show s
99103

100104
-- | Send data to the socket using sendmsg(2).
101105
sendBufMsg
@@ -111,7 +115,9 @@ sendBufMsg
111115
-- ^ Message flags
112116
-> IO Int
113117
-- ^ The length actually sent
114-
sendBufMsg = G.sendBufMsg
118+
sendBufMsg s sa dats cmgs flag =
119+
G.sendBufMsg s sa dats cmgs flag
120+
`annotateIOException` (show s ++ " " ++ show sa)
115121

116122
-- | Receive data from the socket using recvmsg(2).
117123
recvBufMsg
@@ -129,4 +135,4 @@ recvBufMsg
129135
-- ^ Message flags
130136
-> IO (SockAddr, Int, [Cmsg], MsgFlag)
131137
-- ^ Source address, received data, control messages and message flags
132-
recvBufMsg = G.recvBufMsg
138+
recvBufMsg s bufs len flag = G.recvBufMsg s bufs len flag `annotateIOException` show s

Network/Socket/Syscall.hs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,23 @@ socket :: Family -- Family Name (usually AF_INET)
7575
-> SocketType -- Socket Type (usually Stream)
7676
-> ProtocolNumber -- Protocol Number (getProtocolByName to find value)
7777
-> IO Socket -- Unconnected Socket
78-
socket family stype protocol = E.bracketOnError create c_close $ \fd -> do
79-
-- Let's ensure that the socket (file descriptor) is closed even on
80-
-- asynchronous exceptions.
81-
setNonBlock fd
82-
s <- mkSocket fd
83-
-- This socket is not managed by the IO manager yet.
84-
-- So, we don't have to call "close" which uses "closeFdWith".
85-
unsetIPv6Only s
86-
setDontFragment s
87-
return s
78+
socket family stype protocol =
79+
E.bracketOnError create c_close setProp `annotateIOException` (show stype ++ " " ++ show protocol)
8880
where
81+
setProp fd = do
82+
-- Let's ensure that the socket (file descriptor) is closed even on
83+
-- asynchronous exceptions.
84+
setNonBlock fd
85+
s <- mkSocket fd
86+
-- This socket is not managed by the IO manager yet.
87+
-- So, we don't have to call "close" which uses "closeFdWith".
88+
unsetIPv6Only s
89+
setDontFragment s
90+
return s
8991
create = do
9092
let c_stype = modifyFlag $ packSocketType stype
9193
throwSocketErrorIfMinus1Retry "Network.Socket.socket" $
9294
c_socket (packFamily family) c_stype protocol
93-
9495
#ifdef HAVE_ADVANCED_SOCKET_FLAGS
9596
modifyFlag c_stype = c_stype .|. sockNonBlock
9697
#else
@@ -141,7 +142,7 @@ socket family stype protocol = E.bracketOnError create c_close $ \fd -> do
141142
bind :: SocketAddress sa => Socket -> sa -> IO ()
142143
bind s sa = withSocketAddress sa $ \p_sa siz -> void $ withFdSocket s $ \fd -> do
143144
let sz = fromIntegral siz
144-
throwSocketErrorIfMinus1Retry "Network.Socket.bind" $ c_bind fd p_sa sz
145+
throwSocketErrorIfMinus1Retry "Network.Socket.bind" (c_bind fd p_sa sz)
145146

146147
-----------------------------------------------------------------------------
147148
-- Connecting a socket
@@ -181,9 +182,11 @@ connectLoop s p_sa sz = withFdSocket s $ \fd -> loop fd
181182
-- specifies the maximum number of queued connections and should be at
182183
-- least 1; the maximum value is system-dependent (usually 5).
183184
listen :: Socket -> Int -> IO ()
184-
listen s backlog = withFdSocket s $ \fd -> do
185-
throwSocketErrorIfMinus1Retry_ "Network.Socket.listen" $
186-
c_listen fd $ fromIntegral backlog
185+
listen s backlog = withFdSocket s listen' `annotateIOException` show s
186+
where
187+
listen' fd =
188+
throwSocketErrorIfMinus1Retry_ "Network.Socket.listen" $
189+
c_listen fd $ fromIntegral backlog
187190

188191
-----------------------------------------------------------------------------
189192
-- Accept

0 commit comments

Comments
 (0)