-
Notifications
You must be signed in to change notification settings - Fork 188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Making getAddrInfo polymorphic #587
Merged
Merged
Changes from 7 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8c18f94
providing getAddrInfoNE
kazu-yamamoto 152aea0
making getAddrInfo polymorphic
kazu-yamamoto c3fbccc
exporting getAddrInfo only
kazu-yamamoto 795cebb
defining followAddrInfo for NonEmpty
kazu-yamamoto 80fadc1
using the original list code.
kazu-yamamoto 30df86b
improve the error message
kazu-yamamoto 1411634
improving document
kazu-yamamoto ff99e97
Update Network/Socket/Info.hsc
kazu-yamamoto e32a05e
moving the comment (suggested by @endgame)
kazu-yamamoto 34bbbfe
Update Network/Socket/Info.hsc
kazu-yamamoto ae72d08
adding @since
kazu-yamamoto b7ba6ee
Update Network/Socket/Info.hsc
kazu-yamamoto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
|
||
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) | ||
import GHC.IO.Exception (IOErrorType(NoSuchThing)) | ||
|
@@ -200,53 +202,68 @@ defaultHints = AddrInfo { | |
, addrCanonName = Nothing | ||
} | ||
|
||
----------------------------------------------------------------------------- | ||
-- | 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 | ||
|
||
getAddrInfo | ||
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. | ||
-- | ||
-- 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. | ||
kazu-yamamoto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
-- | ||
-- 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
:: 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 | ||
|
||
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 | ||
getAddrInfo 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 -> | ||
|
@@ -257,13 +274,10 @@ getAddrInfo 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 | ||
kazu-yamamoto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
|
@@ -290,13 +304,31 @@ getAddrInfo hints node service = alloc getaddrinfo | |
filteredHints = hints | ||
#endif | ||
|
||
followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo] | ||
getAddrInfoList | ||
:: Maybe AddrInfo | ||
-> Maybe HostName | ||
-> Maybe ServiceName | ||
-> IO [AddrInfo] | ||
getAddrInfoList hints node service = | ||
-- getAddrInfo never returns an empty list. | ||
NE.toList <$> getAddrInfoNE hints node service | ||
|
||
followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) | ||
followAddrInfo ptr_ai | ||
| ptr_ai == nullPtr = return [] | ||
| ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must retuan at least one addrinfo" Nothing Nothing | ||
kazu-yamamoto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
(a :|) <$> go ptr | ||
where | ||
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' | ||
return (a':as') | ||
|
||
foreign import ccall safe "hsnet_getaddrinfo" | ||
c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I rendered the haddocks, and it doesn't give the reader a way to see what instances of
GetAddrInfo
exist. I think we should document them here.Alternatively, we could use the
IsList
class? But I think that will be more confusing and should only be for-XOverloadedLists
support.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.
It seems to me that
IsList
is confusing.1411634 improves the doc.