From 8c18f94c551196c7b40eacdffbed7adafa000b0c Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 30 Aug 2024 13:21:35 +0900 Subject: [PATCH 01/12] providing getAddrInfoNE --- Network/Socket.hs | 7 +++++-- Network/Socket/Info.hsc | 10 ++++++++++ examples/EchoClient.hs | 3 ++- examples/EchoServer.hs | 3 ++- tests/Network/Test/Common.hs | 5 +++-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Network/Socket.hs b/Network/Socket.hs index 8686e82d..7276c61a 100644 --- a/Network/Socket.hs +++ b/Network/Socket.hs @@ -33,6 +33,7 @@ -- > import qualified Control.Exception as E -- > import Control.Monad (unless, forever, void) -- > import qualified Data.ByteString as S +-- > import qualified Data.List.NonEmpty as NE -- > import Network.Socket -- > import Network.Socket.ByteString (recv, sendAll) -- > @@ -56,7 +57,7 @@ -- > addrFlags = [AI_PASSIVE] -- > , addrSocketType = Stream -- > } --- > head <$> getAddrInfo (Just hints) mhost (Just port) +-- > NE.head <$> getAddrInfoNE (Just hints) mhost (Just port) -- > open addr = E.bracketOnError (openSocket addr) close $ \sock -> do -- > setSocketOption sock ReuseAddr 1 -- > withFdSocket sock setCloseOnExecIfNeeded @@ -77,6 +78,7 @@ -- > -- > import qualified Control.Exception as E -- > import qualified Data.ByteString.Char8 as C +-- > import qualified Data.List.NonEmpty as NE -- > import Network.Socket -- > import Network.Socket.ByteString (recv, sendAll) -- > @@ -95,7 +97,7 @@ -- > where -- > resolve = do -- > let hints = defaultHints { addrSocketType = Stream } --- > head <$> getAddrInfo (Just hints) (Just host) (Just port) +-- > NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just port) -- > open addr = E.bracketOnError (openSocket addr) close $ \sock -> do -- > connect sock $ addrAddress addr -- > return sock @@ -111,6 +113,7 @@ module Network.Socket ( -- * Address information getAddrInfo, + getAddrInfoNE, -- ** Types HostName, diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 1a5f2b86..9eb92551 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -7,6 +7,7 @@ module Network.Socket.Info where +import qualified Data.List.NonEmpty as NE import Foreign.Marshal.Alloc (alloca, allocaBytes) import Foreign.Marshal.Utils (maybeWith, with) import GHC.IO.Exception (IOErrorType(NoSuchThing)) @@ -290,6 +291,15 @@ getAddrInfo hints node service = alloc getaddrinfo filteredHints = hints #endif +getAddrInfoNE + :: Maybe AddrInfo + -> Maybe HostName + -> Maybe ServiceName + -> IO (NE.NonEmpty AddrInfo) +getAddrInfoNE hints node service = + -- getAddrInfo never returns an empty list. + NE.fromList <$> getAddrInfo hints node service + followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo] followAddrInfo ptr_ai | ptr_ai == nullPtr = return [] diff --git a/examples/EchoClient.hs b/examples/EchoClient.hs index 3c9f09df..a9e63568 100644 --- a/examples/EchoClient.hs +++ b/examples/EchoClient.hs @@ -5,6 +5,7 @@ module Main (main) where import qualified Control.Exception as E import qualified Data.ByteString.Char8 as C +import qualified Data.List.NonEmpty as NE import Network.Socket import Network.Socket.ByteString (recv, sendAll) @@ -23,7 +24,7 @@ runTCPClient host port client = withSocketsDo $ do where resolve = do let hints = defaultHints{addrSocketType = Stream} - head <$> getAddrInfo (Just hints) (Just host) (Just port) + NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just port) open addr = E.bracketOnError (openSocket addr) close $ \sock -> do connect sock $ addrAddress addr return sock diff --git a/examples/EchoServer.hs b/examples/EchoServer.hs index 6caa4f99..9b242ae5 100644 --- a/examples/EchoServer.hs +++ b/examples/EchoServer.hs @@ -5,6 +5,7 @@ import Control.Concurrent (forkFinally) import qualified Control.Exception as E import Control.Monad (forever, unless, void) import qualified Data.ByteString as S +import qualified Data.List.NonEmpty as NE import Network.Socket import Network.Socket.ByteString (recv, sendAll) @@ -29,7 +30,7 @@ runTCPServer mhost port server = withSocketsDo $ do { addrFlags = [AI_PASSIVE] , addrSocketType = Stream } - head <$> getAddrInfo (Just hints) mhost (Just port) + NE.head <$> getAddrInfoNE (Just hints) mhost (Just port) open addr = E.bracketOnError (openSocket addr) close $ \sock -> do setSocketOption sock ReuseAddr 1 withFdSocket sock setCloseOnExecIfNeeded diff --git a/tests/Network/Test/Common.hs b/tests/Network/Test/Common.hs index b25288b2..333fdc7a 100644 --- a/tests/Network/Test/Common.hs +++ b/tests/Network/Test/Common.hs @@ -35,6 +35,7 @@ import qualified Control.Exception as E import Control.Monad import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as L +import qualified Data.List.NonEmpty as NE import Network.Socket import System.Directory import System.Timeout (timeout) @@ -244,7 +245,7 @@ bracketWithReraise tid setup teardown thing = resolveClient :: SocketType -> HostName -> PortNumber -> IO AddrInfo resolveClient socketType host port = - head <$> getAddrInfo (Just hints) (Just host) (Just $ show port) + NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just $ show port) where hints = defaultHints { addrSocketType = socketType @@ -253,7 +254,7 @@ resolveClient socketType host port = resolveServer :: SocketType -> HostName -> IO AddrInfo resolveServer socketType host = - head <$> getAddrInfo (Just hints) (Just host) Nothing + NE.head <$> getAddrInfoNE (Just hints) (Just host) Nothing where hints = defaultHints { addrSocketType = socketType From 152aea0e1f2f2f6b135f518b48e581e98d828fd6 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 30 Aug 2024 18:48:02 +0900 Subject: [PATCH 02/12] making getAddrInfo polymorphic --- Network/Socket.hs | 7 +++---- Network/Socket/Info.hsc | 17 +++++++++++++++-- examples/EchoClient.hs | 2 +- examples/EchoServer.hs | 2 +- tests/Network/SocketSpec.hs | 2 +- tests/Network/Test/Common.hs | 4 ++-- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Network/Socket.hs b/Network/Socket.hs index 7276c61a..a3a75d51 100644 --- a/Network/Socket.hs +++ b/Network/Socket.hs @@ -57,7 +57,7 @@ -- > addrFlags = [AI_PASSIVE] -- > , addrSocketType = Stream -- > } --- > NE.head <$> getAddrInfoNE (Just hints) mhost (Just port) +-- > NE.head <$> getAddrInfo (Just hints) mhost (Just port) -- > open addr = E.bracketOnError (openSocket addr) close $ \sock -> do -- > setSocketOption sock ReuseAddr 1 -- > withFdSocket sock setCloseOnExecIfNeeded @@ -97,7 +97,7 @@ -- > where -- > resolve = do -- > let hints = defaultHints { addrSocketType = Stream } --- > NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just port) +-- > NE.head <$> getAddrInfo (Just hints) (Just host) (Just port) -- > open addr = E.bracketOnError (openSocket addr) close $ \sock -> do -- > connect sock $ addrAddress addr -- > return sock @@ -112,8 +112,7 @@ module Network.Socket ( withSocketsDo, -- * Address information - getAddrInfo, - getAddrInfoNE, + GetAddrInfo (..), -- ** Types HostName, diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 9eb92551..e9e40791 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -201,6 +201,19 @@ defaultHints = AddrInfo { , addrCanonName = Nothing } +class GetAddrInfo t where + getAddrInfo + :: Maybe AddrInfo -- ^ preferred socket type or protocol + -> Maybe HostName -- ^ host name to look up + -> Maybe ServiceName -- ^ service name to look up + -> IO (t AddrInfo) -- ^ resolved addresses, with "best" first + +instance GetAddrInfo [] where + getAddrInfo = getAddrInfoList + +instance GetAddrInfo NE.NonEmpty where + getAddrInfo = getAddrInfoNE + ----------------------------------------------------------------------------- -- | Resolve a host or service name to one or more addresses. -- The 'AddrInfo' values that this function returns contain 'SockAddr' @@ -242,12 +255,12 @@ defaultHints = AddrInfo { -- >>> addrAddress addr -- 127.0.0.1:80 -getAddrInfo +getAddrInfoList :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up -> Maybe ServiceName -- ^ service name to look up -> IO [AddrInfo] -- ^ resolved addresses, with "best" first -getAddrInfo hints node service = alloc getaddrinfo +getAddrInfoList hints node service = alloc getaddrinfo where alloc body = withSocketsDo $ maybeWith withCString node $ \c_node -> maybeWith withCString service $ \c_service -> diff --git a/examples/EchoClient.hs b/examples/EchoClient.hs index a9e63568..ec72bc7e 100644 --- a/examples/EchoClient.hs +++ b/examples/EchoClient.hs @@ -24,7 +24,7 @@ runTCPClient host port client = withSocketsDo $ do where resolve = do let hints = defaultHints{addrSocketType = Stream} - NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just port) + NE.head <$> getAddrInfo (Just hints) (Just host) (Just port) open addr = E.bracketOnError (openSocket addr) close $ \sock -> do connect sock $ addrAddress addr return sock diff --git a/examples/EchoServer.hs b/examples/EchoServer.hs index 9b242ae5..e760b46f 100644 --- a/examples/EchoServer.hs +++ b/examples/EchoServer.hs @@ -30,7 +30,7 @@ runTCPServer mhost port server = withSocketsDo $ do { addrFlags = [AI_PASSIVE] , addrSocketType = Stream } - NE.head <$> getAddrInfoNE (Just hints) mhost (Just port) + NE.head <$> getAddrInfo (Just hints) mhost (Just port) open addr = E.bracketOnError (openSocket addr) close $ \sock -> do setSocketOption sock ReuseAddr 1 withFdSocket sock setCloseOnExecIfNeeded diff --git a/tests/Network/SocketSpec.hs b/tests/Network/SocketSpec.hs index f2ac337f..7c3ea78b 100644 --- a/tests/Network/SocketSpec.hs +++ b/tests/Network/SocketSpec.hs @@ -118,7 +118,7 @@ spec = do it "does not cause segfault on macOS 10.8.2 due to AI_NUMERICSERV" $ do let hints = defaultHints { addrFlags = [AI_NUMERICSERV] } - void $ getAddrInfo (Just hints) (Just "localhost") Nothing + void (getAddrInfo (Just hints) (Just "localhost") Nothing :: IO [AddrInfo]) #if defined(mingw32_HOST_OS) let lpdevname = "loopback_0" diff --git a/tests/Network/Test/Common.hs b/tests/Network/Test/Common.hs index 333fdc7a..2230a2cd 100644 --- a/tests/Network/Test/Common.hs +++ b/tests/Network/Test/Common.hs @@ -245,7 +245,7 @@ bracketWithReraise tid setup teardown thing = resolveClient :: SocketType -> HostName -> PortNumber -> IO AddrInfo resolveClient socketType host port = - NE.head <$> getAddrInfoNE (Just hints) (Just host) (Just $ show port) + NE.head <$> getAddrInfo (Just hints) (Just host) (Just $ show port) where hints = defaultHints { addrSocketType = socketType @@ -254,7 +254,7 @@ resolveClient socketType host port = resolveServer :: SocketType -> HostName -> IO AddrInfo resolveServer socketType host = - NE.head <$> getAddrInfoNE (Just hints) (Just host) Nothing + NE.head <$> getAddrInfo (Just hints) (Just host) Nothing where hints = defaultHints { addrSocketType = socketType From c3fbccc2f87b86da5b17d16e234f3e5a932f8fd0 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Sat, 31 Aug 2024 04:17:40 +0900 Subject: [PATCH 03/12] exporting getAddrInfo only --- Network/Socket.hs | 2 +- Network/Socket/Info.hsc | 82 +++++++++++++++++++-------------------- Network/Socket/Syscall.hs | 3 +- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Network/Socket.hs b/Network/Socket.hs index a3a75d51..d6665df7 100644 --- a/Network/Socket.hs +++ b/Network/Socket.hs @@ -112,7 +112,7 @@ module Network.Socket ( withSocketsDo, -- * Address information - GetAddrInfo (..), + getAddrInfo, -- ** Types HostName, diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index e9e40791..fa800c3d 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -202,6 +202,47 @@ defaultHints = AddrInfo { } class GetAddrInfo t where + ----------------------------------------------------------------------------- + -- | Resolve a host or service name to one or more addresses. + -- The 'AddrInfo' values that this function returns contain 'SockAddr' + -- values that you can pass directly to 'connect' or + -- 'bind'. + -- + -- This function is protocol independent. It can return both IPv4 and + -- IPv6 address information. + -- + -- The 'AddrInfo' argument specifies the preferred query behaviour, + -- socket options, or protocol. You can override these conveniently + -- using Haskell's record update syntax on 'defaultHints', for example + -- as follows: + -- + -- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST], addrSocketType = Stream } + -- + -- You must provide a 'Just' value for at least one of the 'HostName' + -- or 'ServiceName' arguments. 'HostName' can be either a numeric + -- network address (dotted quad for IPv4, colon-separated hex for + -- IPv6) or a hostname. In the latter case, its addresses will be + -- looked up unless 'AI_NUMERICHOST' is specified as a hint. If you + -- do not provide a 'HostName' value /and/ do not set 'AI_PASSIVE' as + -- a hint, network addresses in the result will contain the address of + -- the loopback interface. + -- + -- If the query fails, this function throws an IO exception instead of + -- returning an empty list. Otherwise, it returns a non-empty list + -- of 'AddrInfo' values. + -- + -- There are several reasons why a query might result in several + -- values. For example, the queried-for host could be multihomed, or + -- the service might be available via several protocols. + -- + -- Note: the order of arguments is slightly different to that defined + -- for @getaddrinfo@ in RFC 2553. The 'AddrInfo' parameter comes first + -- to make partial application easier. + -- + -- >>> import qualified Data.List.NonEmpty as NE + -- >>> addr <- NE.head <$> getAddrInfo (Just hints) (Just "127.0.0.1") (Just "http") + -- >>> addrAddress addr + -- 127.0.0.1:80 getAddrInfo :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up @@ -214,47 +255,6 @@ instance GetAddrInfo [] where instance GetAddrInfo NE.NonEmpty where getAddrInfo = getAddrInfoNE ------------------------------------------------------------------------------ --- | Resolve a host or service name to one or more addresses. --- The 'AddrInfo' values that this function returns contain 'SockAddr' --- values that you can pass directly to 'connect' or --- 'bind'. --- --- This function is protocol independent. It can return both IPv4 and --- IPv6 address information. --- --- The 'AddrInfo' argument specifies the preferred query behaviour, --- socket options, or protocol. You can override these conveniently --- using Haskell's record update syntax on 'defaultHints', for example --- as follows: --- --- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST], addrSocketType = Stream } --- --- You must provide a 'Just' value for at least one of the 'HostName' --- or 'ServiceName' arguments. 'HostName' can be either a numeric --- network address (dotted quad for IPv4, colon-separated hex for --- IPv6) or a hostname. In the latter case, its addresses will be --- looked up unless 'AI_NUMERICHOST' is specified as a hint. If you --- do not provide a 'HostName' value /and/ do not set 'AI_PASSIVE' as --- a hint, network addresses in the result will contain the address of --- the loopback interface. --- --- If the query fails, this function throws an IO exception instead of --- returning an empty list. Otherwise, it returns a non-empty list --- of 'AddrInfo' values. --- --- There are several reasons why a query might result in several --- values. For example, the queried-for host could be multihomed, or --- the service might be available via several protocols. --- --- Note: the order of arguments is slightly different to that defined --- for @getaddrinfo@ in RFC 2553. The 'AddrInfo' parameter comes first --- to make partial application easier. --- --- >>> addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "http") --- >>> addrAddress addr --- 127.0.0.1:80 - getAddrInfoList :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up diff --git a/Network/Socket/Syscall.hs b/Network/Socket/Syscall.hs index b349ea7b..43e07c23 100644 --- a/Network/Socket/Syscall.hs +++ b/Network/Socket/Syscall.hs @@ -63,8 +63,9 @@ import Network.Socket.Types -- can be handled with one socket. -- -- >>> import Network.Socket +-- >>> import qualified Data.List.NonEmpty as NE -- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV], addrSocketType = Stream } --- >>> addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "5000") +-- >>> addr <- NE.head <$> getAddrInfo (Just hints) (Just "127.0.0.1") (Just "5000") -- >>> sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr) -- >>> Network.Socket.bind sock (addrAddress addr) -- >>> getSocketName sock From 795cebbd41f082383fd96c55d96b78d9bd539478 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Sat, 31 Aug 2024 10:39:14 +0900 Subject: [PATCH 04/12] defining followAddrInfo for NonEmpty --- Network/Socket/Info.hsc | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index fa800c3d..1f9b7ac9 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -7,6 +7,7 @@ module Network.Socket.Info where +import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE import Foreign.Marshal.Alloc (alloca, allocaBytes) import Foreign.Marshal.Utils (maybeWith, with) @@ -255,12 +256,12 @@ instance GetAddrInfo [] where instance GetAddrInfo NE.NonEmpty where getAddrInfo = getAddrInfoNE -getAddrInfoList +getAddrInfoNE :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up -> Maybe ServiceName -- ^ service name to look up - -> IO [AddrInfo] -- ^ resolved addresses, with "best" first -getAddrInfoList hints node service = alloc getaddrinfo + -> IO (NonEmpty AddrInfo) -- ^ resolved addresses, with "best" first +getAddrInfoNE hints node service = alloc getaddrinfo where alloc body = withSocketsDo $ maybeWith withCString node $ \c_node -> maybeWith withCString service $ \c_service -> @@ -271,13 +272,10 @@ getAddrInfoList hints node service = alloc getaddrinfo ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs if ret == 0 then do ptr_addrs <- peek ptr_ptr_addrs - ais <- followAddrInfo ptr_addrs - c_freeaddrinfo ptr_addrs -- POSIX requires that getaddrinfo(3) returns at least one addrinfo. -- See: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html - case ais of - [] -> ioError $ mkIOError NoSuchThing message Nothing Nothing - _ -> return ais + ais <- followAddrInfo ptr_addrs + return ais else do err <- gai_strerror ret ioError $ ioeSetErrorString @@ -304,22 +302,31 @@ getAddrInfoList hints node service = alloc getaddrinfo filteredHints = hints #endif -getAddrInfoNE +getAddrInfoList :: Maybe AddrInfo -> Maybe HostName -> Maybe ServiceName - -> IO (NE.NonEmpty AddrInfo) -getAddrInfoNE hints node service = + -> IO [AddrInfo] +getAddrInfoList hints node service = -- getAddrInfo never returns an empty list. - NE.fromList <$> getAddrInfo hints node service + NE.toList <$> getAddrInfoNE hints node service -followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo] +followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) followAddrInfo ptr_ai - | ptr_ai == nullPtr = return [] + | ptr_ai == nullPtr = error "fixme" | otherwise = do - a <- peek ptr_ai - as <- (# peek struct addrinfo, ai_next) ptr_ai >>= followAddrInfo - return (a : as) + a <- peek ptr_ai + ptr <- (# peek struct addrinfo, ai_next) ptr_ai + go ptr a + where + go :: Ptr AddrInfo -> AddrInfo -> IO (NonEmpty AddrInfo) + go ptr a + | ptr == nullPtr = return $ NE.singleton a + | otherwise = do + a' <- peek ptr + ptr' <- (# peek struct addrinfo, ai_next) ptr + as <- go ptr' a' + return $ NE.cons a as foreign import ccall safe "hsnet_getaddrinfo" c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo) From 80fadc1d75e90ddad82df8eba4542c15ef789802 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 4 Sep 2024 07:07:33 +0900 Subject: [PATCH 05/12] using the original list code. --- Network/Socket/Info.hsc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 1f9b7ac9..3ebba95f 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -317,16 +317,16 @@ followAddrInfo ptr_ai | otherwise = do a <- peek ptr_ai ptr <- (# peek struct addrinfo, ai_next) ptr_ai - go ptr a + (a :|) <$> go ptr where - go :: Ptr AddrInfo -> AddrInfo -> IO (NonEmpty AddrInfo) - go ptr a - | ptr == nullPtr = return $ NE.singleton a + go :: Ptr AddrInfo -> IO [AddrInfo] + go ptr + | ptr == nullPtr = return [] | otherwise = do a' <- peek ptr ptr' <- (# peek struct addrinfo, ai_next) ptr - as <- go ptr' a' - return $ NE.cons a as + as' <- go ptr' + return (a':as') foreign import ccall safe "hsnet_getaddrinfo" c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo) From 30df86b35f1c129c99d2412fc2755cf1448aa476 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 4 Sep 2024 07:12:52 +0900 Subject: [PATCH 06/12] improve the error message --- Network/Socket/Info.hsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 3ebba95f..260b42aa 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -313,7 +313,7 @@ getAddrInfoList hints node service = followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) followAddrInfo ptr_ai - | ptr_ai == nullPtr = error "fixme" + | ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must retuan at least one addrinfo" Nothing Nothing | otherwise = do a <- peek ptr_ai ptr <- (# peek struct addrinfo, ai_next) ptr_ai From 1411634cd1dcf7c04cebcf26103ea6aea0038c7f Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 4 Sep 2024 14:20:01 +0900 Subject: [PATCH 07/12] improving document --- Network/Socket/Info.hsc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 260b42aa..72f212a1 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -207,7 +207,9 @@ class GetAddrInfo t where -- | Resolve a host or service name to one or more addresses. -- The 'AddrInfo' values that this function returns contain 'SockAddr' -- values that you can pass directly to 'connect' or - -- 'bind'. + -- 'bind'. Instances for the hidden 'GetAddrInfo' class are lists and + -- 'NonEmpty' only. Use of 'NonEmpty' is recommended as getaddrinfo() never + -- returns an empty list. -- -- This function is protocol independent. It can return both IPv4 and -- IPv6 address information. From ff99e970111da5eddab4c7d519e78102c8b7873d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 6 Sep 2024 11:21:43 +0900 Subject: [PATCH 08/12] Update Network/Socket/Info.hsc Co-authored-by: endgame --- Network/Socket/Info.hsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 72f212a1..c16dbdc0 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -315,7 +315,7 @@ getAddrInfoList hints node service = followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) followAddrInfo ptr_ai - | ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must retuan at least one addrinfo" Nothing Nothing + | ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must return at least one addrinfo" Nothing Nothing | otherwise = do a <- peek ptr_ai ptr <- (# peek struct addrinfo, ai_next) ptr_ai From e32a05e2434ea638bea9e32b50629c0d5a418a4a Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 6 Sep 2024 11:23:45 +0900 Subject: [PATCH 09/12] moving the comment (suggested by @endgame) --- Network/Socket/Info.hsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index c16dbdc0..4a7303b8 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -274,8 +274,6 @@ getAddrInfoNE hints node service = alloc getaddrinfo ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs if ret == 0 then do ptr_addrs <- peek ptr_ptr_addrs - -- POSIX requires that getaddrinfo(3) returns at least one addrinfo. - -- See: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html ais <- followAddrInfo ptr_addrs return ais else do @@ -315,6 +313,8 @@ getAddrInfoList hints node service = followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) followAddrInfo ptr_ai + -- POSIX requires that getaddrinfo(3) returns at least one addrinfo. + -- See: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html | ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must return at least one addrinfo" Nothing Nothing | otherwise = do a <- peek ptr_ai From 34bbbfe030c885d1a4ee11c05337b68759b0ec5a Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 6 Sep 2024 11:26:12 +0900 Subject: [PATCH 10/12] Update Network/Socket/Info.hsc Co-authored-by: endgame --- Network/Socket/Info.hsc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 4a7303b8..669f9165 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -207,9 +207,15 @@ class GetAddrInfo t where -- | Resolve a host or service name to one or more addresses. -- The 'AddrInfo' values that this function returns contain 'SockAddr' -- values that you can pass directly to 'connect' or - -- 'bind'. Instances for the hidden 'GetAddrInfo' class are lists and - -- 'NonEmpty' only. Use of 'NonEmpty' is recommended as getaddrinfo() never - -- returns an empty list. + -- 'bind'. + -- + -- This function calls @getaddrinfo(3)@, which never successfully returns + -- with an empty list. If the query fails, 'getAddrInfo' throws an IO + -- exception. + -- + -- For backwards-compatibility reasons, a hidden 'GetAddrInfo' class is used + -- to make the result polymorphic. It only has instances for @[]@ (lists) + -- and 'NonEmpty' only. Use of 'NonEmpty' is recommended. -- -- This function is protocol independent. It can return both IPv4 and -- IPv6 address information. @@ -230,10 +236,6 @@ class GetAddrInfo t where -- a hint, network addresses in the result will contain the address of -- the loopback interface. -- - -- If the query fails, this function throws an IO exception instead of - -- returning an empty list. Otherwise, it returns a non-empty list - -- of 'AddrInfo' values. - -- -- There are several reasons why a query might result in several -- values. For example, the queried-for host could be multihomed, or -- the service might be available via several protocols. From ae72d0865b97dd26b44177ceddfdf9fb38383e95 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 6 Sep 2024 11:28:42 +0900 Subject: [PATCH 11/12] adding @since --- Network/Socket/Info.hsc | 2 ++ network.cabal | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 669f9165..78efb349 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -248,6 +248,8 @@ class GetAddrInfo t where -- >>> addr <- NE.head <$> getAddrInfo (Just hints) (Just "127.0.0.1") (Just "http") -- >>> addrAddress addr -- 127.0.0.1:80 + -- + -- Polymorphic version: @since 3.2.3.0 getAddrInfo :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up diff --git a/network.cabal b/network.cabal index 391b9674..a8c42a02 100644 --- a/network.cabal +++ b/network.cabal @@ -1,6 +1,6 @@ cabal-version: 1.18 name: network -version: 3.2.2.0 +version: 3.2.3.0 license: BSD3 license-file: LICENSE maintainer: Kazu Yamamoto, Tamar Christina From b7ba6eeb6aeb66ef443436b6e155f01cb36a3d0d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 11 Sep 2024 09:28:30 +0900 Subject: [PATCH 12/12] Update Network/Socket/Info.hsc Co-authored-by: endgame --- Network/Socket/Info.hsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index 78efb349..b3039ce4 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -215,7 +215,7 @@ class GetAddrInfo t where -- -- For backwards-compatibility reasons, a hidden 'GetAddrInfo' class is used -- to make the result polymorphic. It only has instances for @[]@ (lists) - -- and 'NonEmpty' only. Use of 'NonEmpty' is recommended. + -- and 'NonEmpty'. Use of 'NonEmpty' is recommended. -- -- This function is protocol independent. It can return both IPv4 and -- IPv6 address information.