{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Network.Connection
(
Connection
, connectionID
, ConnectionParams(..)
, TLSSettings(..)
, ProxySettings(..)
, SockSettings
, LineTooLong(..)
, HostNotResolved(..)
, HostCannotConnect(..)
, initConnectionContext
, ConnectionContext
, connectFromHandle
, connectFromSocket
, connectTo
, connectionClose
, connectionGet
, connectionGetExact
, connectionGetChunk
, connectionGetChunk'
, connectionGetLine
, connectionWaitForInput
, connectionPut
, connectionSetSecure
, connectionIsSecure
, connectionSessionManager
) where
import Control.Concurrent.MVar
import Control.Monad (join)
import qualified Control.Exception as E
import qualified System.IO.Error as E (mkIOError, eofErrorType)
import qualified Network.TLS as TLS
import System.X509 (getSystemCertificateStore)
import Network.Socks5 (defaultSocksConf, socksConnectWithSocket, SocksAddress(..), SocksHostAddress(..))
import Network.Socket
import qualified Network.Socket.ByteString as N
import Data.Tuple (swap)
import Data.Default.Class
import Data.Data
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as L
import System.Environment
import System.Timeout
import System.IO
import qualified Data.Map as M
import Network.Connection.Types
type Manager = MVar (M.Map TLS.SessionID TLS.SessionData)
data LineTooLong = LineTooLong deriving (Int -> LineTooLong -> ShowS
[LineTooLong] -> ShowS
LineTooLong -> String
(Int -> LineTooLong -> ShowS)
-> (LineTooLong -> String)
-> ([LineTooLong] -> ShowS)
-> Show LineTooLong
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> LineTooLong -> ShowS
showsPrec :: Int -> LineTooLong -> ShowS
$cshow :: LineTooLong -> String
show :: LineTooLong -> String
$cshowList :: [LineTooLong] -> ShowS
showList :: [LineTooLong] -> ShowS
Show,Typeable)
data HostNotResolved = HostNotResolved String deriving (Int -> HostNotResolved -> ShowS
[HostNotResolved] -> ShowS
HostNotResolved -> String
(Int -> HostNotResolved -> ShowS)
-> (HostNotResolved -> String)
-> ([HostNotResolved] -> ShowS)
-> Show HostNotResolved
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> HostNotResolved -> ShowS
showsPrec :: Int -> HostNotResolved -> ShowS
$cshow :: HostNotResolved -> String
show :: HostNotResolved -> String
$cshowList :: [HostNotResolved] -> ShowS
showList :: [HostNotResolved] -> ShowS
Show,Typeable)
data HostCannotConnect = HostCannotConnect String [E.IOException] deriving (Int -> HostCannotConnect -> ShowS
[HostCannotConnect] -> ShowS
HostCannotConnect -> String
(Int -> HostCannotConnect -> ShowS)
-> (HostCannotConnect -> String)
-> ([HostCannotConnect] -> ShowS)
-> Show HostCannotConnect
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> HostCannotConnect -> ShowS
showsPrec :: Int -> HostCannotConnect -> ShowS
$cshow :: HostCannotConnect -> String
show :: HostCannotConnect -> String
$cshowList :: [HostCannotConnect] -> ShowS
showList :: [HostCannotConnect] -> ShowS
Show,Typeable)
instance E.Exception LineTooLong
instance E.Exception HostNotResolved
instance E.Exception HostCannotConnect
connectionSessionManager :: Manager -> TLS.SessionManager
connectionSessionManager :: Manager -> SessionManager
connectionSessionManager Manager
mvar = SessionManager
TLS.noSessionManager
{ TLS.sessionResume = \SessionID
sessionID -> Manager
-> (Map SessionID SessionData -> IO (Maybe SessionData))
-> IO (Maybe SessionData)
forall a b. MVar a -> (a -> IO b) -> IO b
withMVar Manager
mvar (Maybe SessionData -> IO (Maybe SessionData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe SessionData -> IO (Maybe SessionData))
-> (Map SessionID SessionData -> Maybe SessionData)
-> Map SessionID SessionData
-> IO (Maybe SessionData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SessionID -> Map SessionID SessionData -> Maybe SessionData
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup SessionID
sessionID)
, TLS.sessionEstablish = \SessionID
sessionID SessionData
sessionData ->
Manager
-> (Map SessionID SessionData -> IO (Map SessionID SessionData))
-> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ Manager
mvar (Map SessionID SessionData -> IO (Map SessionID SessionData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Map SessionID SessionData -> IO (Map SessionID SessionData))
-> (Map SessionID SessionData -> Map SessionID SessionData)
-> Map SessionID SessionData
-> IO (Map SessionID SessionData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SessionID
-> SessionData
-> Map SessionID SessionData
-> Map SessionID SessionData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert SessionID
sessionID SessionData
sessionData)
#if MIN_VERSION_tls(2,0,0)
IO () -> IO (Maybe SessionID) -> IO (Maybe SessionID)
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe SessionID -> IO (Maybe SessionID)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe SessionID
forall a. Maybe a
Nothing
#endif
, TLS.sessionInvalidate = \SessionID
sessionID -> Manager
-> (Map SessionID SessionData -> IO (Map SessionID SessionData))
-> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ Manager
mvar (Map SessionID SessionData -> IO (Map SessionID SessionData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Map SessionID SessionData -> IO (Map SessionID SessionData))
-> (Map SessionID SessionData -> Map SessionID SessionData)
-> Map SessionID SessionData
-> IO (Map SessionID SessionData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SessionID -> Map SessionID SessionData -> Map SessionID SessionData
forall k a. Ord k => k -> Map k a -> Map k a
M.delete SessionID
sessionID)
#if MIN_VERSION_tls(1,5,0)
, TLS.sessionResumeOnlyOnce = \SessionID
sessionID ->
Manager
-> (Map SessionID SessionData
-> IO (Map SessionID SessionData, Maybe SessionData))
-> IO (Maybe SessionData)
forall a b. MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar Manager
mvar ((Map SessionID SessionData, Maybe SessionData)
-> IO (Map SessionID SessionData, Maybe SessionData)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((Map SessionID SessionData, Maybe SessionData)
-> IO (Map SessionID SessionData, Maybe SessionData))
-> (Map SessionID SessionData
-> (Map SessionID SessionData, Maybe SessionData))
-> Map SessionID SessionData
-> IO (Map SessionID SessionData, Maybe SessionData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe SessionData, Map SessionID SessionData)
-> (Map SessionID SessionData, Maybe SessionData)
forall a b. (a, b) -> (b, a)
swap ((Maybe SessionData, Map SessionID SessionData)
-> (Map SessionID SessionData, Maybe SessionData))
-> (Map SessionID SessionData
-> (Maybe SessionData, Map SessionID SessionData))
-> Map SessionID SessionData
-> (Map SessionID SessionData, Maybe SessionData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SessionID -> SessionData -> Maybe SessionData)
-> SessionID
-> Map SessionID SessionData
-> (Maybe SessionData, Map SessionID SessionData)
forall k a.
Ord k =>
(k -> a -> Maybe a) -> k -> Map k a -> (Maybe a, Map k a)
M.updateLookupWithKey (\SessionID
_ SessionData
_ -> Maybe SessionData
forall a. Maybe a
Nothing) SessionID
sessionID)
#endif
#if MIN_VERSION_tls(2,0,0)
, TLS.sessionUseTicket = False
#endif
}
initConnectionContext :: IO ConnectionContext
initConnectionContext :: IO ConnectionContext
initConnectionContext = CertificateStore -> ConnectionContext
ConnectionContext (CertificateStore -> ConnectionContext)
-> IO CertificateStore -> IO ConnectionContext
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO CertificateStore
getSystemCertificateStore
makeTLSParams :: ConnectionContext -> ConnectionID -> TLSSettings -> TLS.ClientParams
makeTLSParams :: ConnectionContext -> ConnectionID -> TLSSettings -> ClientParams
makeTLSParams ConnectionContext
cg ConnectionID
cid ts :: TLSSettings
ts@(TLSSettingsSimple {}) =
(String -> SessionID -> ClientParams
TLS.defaultParamsClient (ConnectionID -> String
forall a b. (a, b) -> a
fst ConnectionID
cid) SessionID
portString)
{ TLS.clientSupported = settingClientSupported ts
, TLS.clientShared = def
{ TLS.sharedCAStore = globalCertificateStore cg
, TLS.sharedValidationCache = validationCache
}
}
where validationCache :: ValidationCache
validationCache
| TLSSettings -> Bool
settingDisableCertificateValidation TLSSettings
ts =
ValidationCacheQueryCallback
-> ValidationCacheAddCallback -> ValidationCache
TLS.ValidationCache (\ServiceID
_ Fingerprint
_ Certificate
_ -> ValidationCacheResult -> IO ValidationCacheResult
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ValidationCacheResult
TLS.ValidationCachePass)
(\ServiceID
_ Fingerprint
_ Certificate
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ())
| Bool
otherwise = ValidationCache
forall a. Default a => a
def
portString :: SessionID
portString = String -> SessionID
BC.pack (String -> SessionID) -> String -> SessionID
forall a b. (a -> b) -> a -> b
$ PortNumber -> String
forall a. Show a => a -> String
show (PortNumber -> String) -> PortNumber -> String
forall a b. (a -> b) -> a -> b
$ ConnectionID -> PortNumber
forall a b. (a, b) -> b
snd ConnectionID
cid
makeTLSParams ConnectionContext
_ ConnectionID
cid (TLSSettings ClientParams
p) =
ClientParams
p { TLS.clientServerIdentification = (fst cid, portString) }
where portString :: SessionID
portString = String -> SessionID
BC.pack (String -> SessionID) -> String -> SessionID
forall a b. (a -> b) -> a -> b
$ PortNumber -> String
forall a. Show a => a -> String
show (PortNumber -> String) -> PortNumber -> String
forall a b. (a -> b) -> a -> b
$ ConnectionID -> PortNumber
forall a b. (a, b) -> b
snd ConnectionID
cid
withBackend :: (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend :: forall a. (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend ConnectionBackend -> IO a
f Connection
conn = MVar ConnectionBackend -> IO ConnectionBackend
forall a. MVar a -> IO a
readMVar (Connection -> MVar ConnectionBackend
connectionBackend Connection
conn) IO ConnectionBackend -> (ConnectionBackend -> IO a) -> IO a
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConnectionBackend -> IO a
f
connectionNew :: ConnectionID -> ConnectionBackend -> IO Connection
connectionNew :: ConnectionID -> ConnectionBackend -> IO Connection
connectionNew ConnectionID
cid ConnectionBackend
backend =
MVar ConnectionBackend
-> MVar (Maybe SessionID) -> ConnectionID -> Connection
Connection (MVar ConnectionBackend
-> MVar (Maybe SessionID) -> ConnectionID -> Connection)
-> IO (MVar ConnectionBackend)
-> IO (MVar (Maybe SessionID) -> ConnectionID -> Connection)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ConnectionBackend -> IO (MVar ConnectionBackend)
forall a. a -> IO (MVar a)
newMVar ConnectionBackend
backend
IO (MVar (Maybe SessionID) -> ConnectionID -> Connection)
-> IO (MVar (Maybe SessionID)) -> IO (ConnectionID -> Connection)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe SessionID -> IO (MVar (Maybe SessionID))
forall a. a -> IO (MVar a)
newMVar (SessionID -> Maybe SessionID
forall a. a -> Maybe a
Just SessionID
B.empty)
IO (ConnectionID -> Connection) -> IO ConnectionID -> IO Connection
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ConnectionID -> IO ConnectionID
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ConnectionID
cid
connectFromHandle :: ConnectionContext
-> Handle
-> ConnectionParams
-> IO Connection
connectFromHandle :: ConnectionContext -> Handle -> ConnectionParams -> IO Connection
connectFromHandle ConnectionContext
cg Handle
h ConnectionParams
p = Maybe TLSSettings -> IO Connection
withSecurity (ConnectionParams -> Maybe TLSSettings
connectionUseSecure ConnectionParams
p)
where withSecurity :: Maybe TLSSettings -> IO Connection
withSecurity Maybe TLSSettings
Nothing = ConnectionID -> ConnectionBackend -> IO Connection
connectionNew ConnectionID
cid (ConnectionBackend -> IO Connection)
-> ConnectionBackend -> IO Connection
forall a b. (a -> b) -> a -> b
$ Handle -> ConnectionBackend
ConnectionStream Handle
h
withSecurity (Just TLSSettings
tlsSettings) = Handle -> ClientParams -> IO Context
forall backend.
HasBackend backend =>
backend -> ClientParams -> IO Context
tlsEstablish Handle
h (ConnectionContext -> ConnectionID -> TLSSettings -> ClientParams
makeTLSParams ConnectionContext
cg ConnectionID
cid TLSSettings
tlsSettings) IO Context -> (Context -> IO Connection) -> IO Connection
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConnectionID -> ConnectionBackend -> IO Connection
connectionNew ConnectionID
cid (ConnectionBackend -> IO Connection)
-> (Context -> ConnectionBackend) -> Context -> IO Connection
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Context -> ConnectionBackend
ConnectionTLS
cid :: ConnectionID
cid = (ConnectionParams -> String
connectionHostname ConnectionParams
p, ConnectionParams -> PortNumber
connectionPort ConnectionParams
p)
connectFromSocket :: ConnectionContext
-> Socket
-> ConnectionParams
-> IO Connection
connectFromSocket :: ConnectionContext -> Socket -> ConnectionParams -> IO Connection
connectFromSocket ConnectionContext
cg Socket
sock ConnectionParams
p = Maybe TLSSettings -> IO Connection
withSecurity (ConnectionParams -> Maybe TLSSettings
connectionUseSecure ConnectionParams
p)
where withSecurity :: Maybe TLSSettings -> IO Connection
withSecurity Maybe TLSSettings
Nothing = ConnectionID -> ConnectionBackend -> IO Connection
connectionNew ConnectionID
cid (ConnectionBackend -> IO Connection)
-> ConnectionBackend -> IO Connection
forall a b. (a -> b) -> a -> b
$ Socket -> ConnectionBackend
ConnectionSocket Socket
sock
withSecurity (Just TLSSettings
tlsSettings) = Socket -> ClientParams -> IO Context
forall backend.
HasBackend backend =>
backend -> ClientParams -> IO Context
tlsEstablish Socket
sock (ConnectionContext -> ConnectionID -> TLSSettings -> ClientParams
makeTLSParams ConnectionContext
cg ConnectionID
cid TLSSettings
tlsSettings) IO Context -> (Context -> IO Connection) -> IO Connection
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConnectionID -> ConnectionBackend -> IO Connection
connectionNew ConnectionID
cid (ConnectionBackend -> IO Connection)
-> (Context -> ConnectionBackend) -> Context -> IO Connection
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Context -> ConnectionBackend
ConnectionTLS
cid :: ConnectionID
cid = (ConnectionParams -> String
connectionHostname ConnectionParams
p, ConnectionParams -> PortNumber
connectionPort ConnectionParams
p)
connectTo :: ConnectionContext
-> ConnectionParams
-> IO Connection
connectTo :: ConnectionContext -> ConnectionParams -> IO Connection
connectTo ConnectionContext
cg ConnectionParams
cParams = do
let conFct :: IO (Socket, SockAddr)
conFct = Maybe ProxySettings
-> String -> PortNumber -> IO (Socket, SockAddr)
doConnect (ConnectionParams -> Maybe ProxySettings
connectionUseSocks ConnectionParams
cParams)
(ConnectionParams -> String
connectionHostname ConnectionParams
cParams)
(ConnectionParams -> PortNumber
connectionPort ConnectionParams
cParams)
IO (Socket, SockAddr)
-> ((Socket, SockAddr) -> IO ())
-> ((Socket, SockAddr) -> IO Connection)
-> IO Connection
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracketOnError IO (Socket, SockAddr)
conFct (Socket -> IO ()
close (Socket -> IO ())
-> ((Socket, SockAddr) -> Socket) -> (Socket, SockAddr) -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Socket, SockAddr) -> Socket
forall a b. (a, b) -> a
fst) (((Socket, SockAddr) -> IO Connection) -> IO Connection)
-> ((Socket, SockAddr) -> IO Connection) -> IO Connection
forall a b. (a -> b) -> a -> b
$ \(Socket
h, SockAddr
_) ->
ConnectionContext -> Socket -> ConnectionParams -> IO Connection
connectFromSocket ConnectionContext
cg Socket
h ConnectionParams
cParams
where
sockConnect :: String
-> PortNumber -> String -> PortNumber -> IO (Socket, SockAddr)
sockConnect String
sockHost PortNumber
sockPort String
h PortNumber
p = do
(Socket
sockServ, SockAddr
servAddr) <- String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
sockHost PortNumber
sockPort
let sockConf :: SocksConf
sockConf = SockAddr -> SocksConf
defaultSocksConf SockAddr
servAddr
let destAddr :: SocksAddress
destAddr = SocksHostAddress -> PortNumber -> SocksAddress
SocksAddress (SessionID -> SocksHostAddress
SocksAddrDomainName (SessionID -> SocksHostAddress) -> SessionID -> SocksHostAddress
forall a b. (a -> b) -> a -> b
$ String -> SessionID
BC.pack String
h) PortNumber
p
(SocksHostAddress
dest, PortNumber
_) <- Socket
-> SocksConf -> SocksAddress -> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket Socket
sockServ SocksConf
sockConf SocksAddress
destAddr
case SocksHostAddress
dest of
SocksAddrIPV4 FlowInfo
h4 -> (Socket, SockAddr) -> IO (Socket, SockAddr)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Socket
sockServ, PortNumber -> FlowInfo -> SockAddr
SockAddrInet PortNumber
p FlowInfo
h4)
SocksAddrIPV6 HostAddress6
h6 -> (Socket, SockAddr) -> IO (Socket, SockAddr)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Socket
sockServ, PortNumber -> FlowInfo -> HostAddress6 -> FlowInfo -> SockAddr
SockAddrInet6 PortNumber
p FlowInfo
0 HostAddress6
h6 FlowInfo
0)
SocksAddrDomainName SessionID
_ -> String -> IO (Socket, SockAddr)
forall a. HasCallStack => String -> a
error String
"internal error: socks connect return a resolved address as domain name"
doConnect :: Maybe ProxySettings
-> String -> PortNumber -> IO (Socket, SockAddr)
doConnect Maybe ProxySettings
proxy String
h PortNumber
p =
case Maybe ProxySettings
proxy of
Maybe ProxySettings
Nothing -> String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
h PortNumber
p
Just (OtherProxy String
proxyHost PortNumber
proxyPort) -> String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
proxyHost PortNumber
proxyPort
Just (SockSettingsSimple String
sockHost PortNumber
sockPort) ->
String
-> PortNumber -> String -> PortNumber -> IO (Socket, SockAddr)
sockConnect String
sockHost PortNumber
sockPort String
h PortNumber
p
Just (SockSettingsEnvironment Maybe String
envName) -> do
let name :: String
name = String -> ShowS -> Maybe String -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"SOCKS_SERVER" ShowS
forall a. a -> a
id Maybe String
envName
Either IOException String
evar <- IO String -> IO (Either IOException String)
forall e a. Exception e => IO a -> IO (Either e a)
E.try (String -> IO String
getEnv String
name)
case Either IOException String
evar of
Left (IOException
_ :: E.IOException) -> String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
h PortNumber
p
Right String
var ->
case String -> Maybe ConnectionID
parseSocks String
var of
Maybe ConnectionID
Nothing -> String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
h PortNumber
p
Just (String
sockHost, PortNumber
sockPort) -> String
-> PortNumber -> String -> PortNumber -> IO (Socket, SockAddr)
sockConnect String
sockHost PortNumber
sockPort String
h PortNumber
p
parseSocks :: String -> Maybe (String, PortNumber)
parseSocks :: String -> Maybe ConnectionID
parseSocks String
s =
case (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
':') String
s of
(String
sHost, String
"") -> ConnectionID -> Maybe ConnectionID
forall a. a -> Maybe a
Just (String
sHost, PortNumber
1080)
(String
sHost, Char
':':String
portS) ->
case ReadS PortNumber
forall a. Read a => ReadS a
reads String
portS of
[(PortNumber
sPort,String
"")] -> ConnectionID -> Maybe ConnectionID
forall a. a -> Maybe a
Just (String
sHost, PortNumber
sPort)
[(PortNumber, String)]
_ -> Maybe ConnectionID
forall a. Maybe a
Nothing
(String, String)
_ -> Maybe ConnectionID
forall a. Maybe a
Nothing
resolve' :: String -> PortNumber -> IO (Socket, SockAddr)
resolve' :: String -> PortNumber -> IO (Socket, SockAddr)
resolve' String
host PortNumber
port = do
let hints :: AddrInfo
hints = AddrInfo
defaultHints { addrFlags = [AI_ADDRCONFIG], addrSocketType = Stream }
[AddrInfo]
addrs <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
getAddrInfo (AddrInfo -> Maybe AddrInfo
forall a. a -> Maybe a
Just AddrInfo
hints) (String -> Maybe String
forall a. a -> Maybe a
Just String
host) (String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ PortNumber -> String
forall a. Show a => a -> String
show PortNumber
port)
[IO (Socket, SockAddr)] -> IO (Socket, SockAddr)
forall {a}. [IO a] -> IO a
firstSuccessful ([IO (Socket, SockAddr)] -> IO (Socket, SockAddr))
-> [IO (Socket, SockAddr)] -> IO (Socket, SockAddr)
forall a b. (a -> b) -> a -> b
$ (AddrInfo -> IO (Socket, SockAddr))
-> [AddrInfo] -> [IO (Socket, SockAddr)]
forall a b. (a -> b) -> [a] -> [b]
map AddrInfo -> IO (Socket, SockAddr)
tryToConnect [AddrInfo]
addrs
where
tryToConnect :: AddrInfo -> IO (Socket, SockAddr)
tryToConnect AddrInfo
addr =
IO Socket
-> (Socket -> IO ())
-> (Socket -> IO (Socket, SockAddr))
-> IO (Socket, SockAddr)
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracketOnError
(Family -> SocketType -> ProtocolNumber -> IO Socket
socket (AddrInfo -> Family
addrFamily AddrInfo
addr) (AddrInfo -> SocketType
addrSocketType AddrInfo
addr) (AddrInfo -> ProtocolNumber
addrProtocol AddrInfo
addr))
(Socket -> IO ()
close)
(\Socket
sock -> Socket -> SockAddr -> IO ()
connect Socket
sock (AddrInfo -> SockAddr
addrAddress AddrInfo
addr) IO () -> IO (Socket, SockAddr) -> IO (Socket, SockAddr)
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (Socket, SockAddr) -> IO (Socket, SockAddr)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Socket
sock, AddrInfo -> SockAddr
addrAddress AddrInfo
addr))
firstSuccessful :: [IO a] -> IO a
firstSuccessful = [IOException] -> [IO a] -> IO a
forall a. [IOException] -> [IO a] -> IO a
go []
where
go :: [E.IOException] -> [IO a] -> IO a
go :: forall a. [IOException] -> [IO a] -> IO a
go [] [] = HostNotResolved -> IO a
forall e a. Exception e => e -> IO a
E.throwIO (HostNotResolved -> IO a) -> HostNotResolved -> IO a
forall a b. (a -> b) -> a -> b
$ String -> HostNotResolved
HostNotResolved String
host
go l :: [IOException]
l@(IOException
_:[IOException]
_) [] = HostCannotConnect -> IO a
forall e a. Exception e => e -> IO a
E.throwIO (HostCannotConnect -> IO a) -> HostCannotConnect -> IO a
forall a b. (a -> b) -> a -> b
$ String -> [IOException] -> HostCannotConnect
HostCannotConnect String
host [IOException]
l
go [IOException]
acc (IO a
act:[IO a]
followingActs) = do
Either IOException a
er <- IO a -> IO (Either IOException a)
forall e a. Exception e => IO a -> IO (Either e a)
E.try IO a
act
case Either IOException a
er of
Left IOException
err -> [IOException] -> [IO a] -> IO a
forall a. [IOException] -> [IO a] -> IO a
go (IOException
errIOException -> [IOException] -> [IOException]
forall a. a -> [a] -> [a]
:[IOException]
acc) [IO a]
followingActs
Right a
r -> a -> IO a
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
r
connectionPut :: Connection -> ByteString -> IO ()
connectionPut :: Connection -> SessionID -> IO ()
connectionPut Connection
connection SessionID
content = (ConnectionBackend -> IO ()) -> Connection -> IO ()
forall a. (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend ConnectionBackend -> IO ()
doWrite Connection
connection
where doWrite :: ConnectionBackend -> IO ()
doWrite (ConnectionStream Handle
h) = Handle -> SessionID -> IO ()
B.hPut Handle
h SessionID
content IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
h
doWrite (ConnectionSocket Socket
s) = Socket -> SessionID -> IO ()
N.sendAll Socket
s SessionID
content
doWrite (ConnectionTLS Context
ctx) = Context -> ByteString -> IO ()
forall (m :: * -> *). MonadIO m => Context -> ByteString -> m ()
TLS.sendData Context
ctx (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ [SessionID] -> ByteString
L.fromChunks [SessionID
content]
connectionGetExact :: Connection -> Int -> IO ByteString
connectionGetExact :: Connection -> Int -> IO SessionID
connectionGetExact Connection
conn Int
x = SessionID -> Int -> IO SessionID
loop SessionID
B.empty Int
0
where loop :: SessionID -> Int -> IO SessionID
loop SessionID
bs Int
y
| Int
y Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
x = SessionID -> IO SessionID
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return SessionID
bs
| Bool
otherwise = do
SessionID
next <- Connection -> Int -> IO SessionID
connectionGet Connection
conn (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
y)
SessionID -> Int -> IO SessionID
loop (SessionID -> SessionID -> SessionID
B.append SessionID
bs SessionID
next) (Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (SessionID -> Int
B.length SessionID
next))
connectionGet :: Connection -> Int -> IO ByteString
connectionGet :: Connection -> Int -> IO SessionID
connectionGet Connection
conn Int
size
| Int
size Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> IO SessionID
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Network.Connection.connectionGet: size < 0"
| Int
size Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = SessionID -> IO SessionID
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return SessionID
B.empty
| Bool
otherwise = String
-> Connection
-> (SessionID -> (SessionID, SessionID))
-> IO SessionID
forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
"connectionGet" Connection
conn ((SessionID -> (SessionID, SessionID)) -> IO SessionID)
-> (SessionID -> (SessionID, SessionID)) -> IO SessionID
forall a b. (a -> b) -> a -> b
$ Int -> SessionID -> (SessionID, SessionID)
B.splitAt Int
size
connectionGetChunk :: Connection -> IO ByteString
connectionGetChunk :: Connection -> IO SessionID
connectionGetChunk Connection
conn =
String
-> Connection
-> (SessionID -> (SessionID, SessionID))
-> IO SessionID
forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
"connectionGetChunk" Connection
conn ((SessionID -> (SessionID, SessionID)) -> IO SessionID)
-> (SessionID -> (SessionID, SessionID)) -> IO SessionID
forall a b. (a -> b) -> a -> b
$ \SessionID
s -> (SessionID
s, SessionID
B.empty)
connectionGetChunk' :: Connection -> (ByteString -> (a, ByteString)) -> IO a
connectionGetChunk' :: forall a. Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunk' = String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
"connectionGetChunk'"
connectionWaitForInput :: Connection -> Int -> IO Bool
connectionWaitForInput :: Connection -> Int -> IO Bool
connectionWaitForInput Connection
conn Int
timeout_ms = Bool -> (() -> Bool) -> Maybe () -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (Bool -> () -> Bool
forall a b. a -> b -> a
const Bool
True) (Maybe () -> Bool) -> IO (Maybe ()) -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> IO () -> IO (Maybe ())
forall a. Int -> IO a -> IO (Maybe a)
timeout Int
timeout_ns IO ()
tryGetChunk
where tryGetChunk :: IO ()
tryGetChunk = String -> Connection -> (SessionID -> ((), SessionID)) -> IO ()
forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
"connectionWaitForInput" Connection
conn ((SessionID -> ((), SessionID)) -> IO ())
-> (SessionID -> ((), SessionID)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \SessionID
buf -> ((), SessionID
buf)
timeout_ns :: Int
timeout_ns = Int
timeout_ms Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1000
connectionGetChunkBase :: String -> Connection -> (ByteString -> (a, ByteString)) -> IO a
connectionGetChunkBase :: forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
loc Connection
conn SessionID -> (a, SessionID)
f =
MVar (Maybe SessionID)
-> (Maybe SessionID -> IO (Maybe SessionID, a)) -> IO a
forall a b. MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar (Connection -> MVar (Maybe SessionID)
connectionBuffer Connection
conn) ((Maybe SessionID -> IO (Maybe SessionID, a)) -> IO a)
-> (Maybe SessionID -> IO (Maybe SessionID, a)) -> IO a
forall a b. (a -> b) -> a -> b
$ \Maybe SessionID
m ->
case Maybe SessionID
m of
Maybe SessionID
Nothing -> Connection -> String -> IO (Maybe SessionID, a)
forall a. Connection -> String -> IO a
throwEOF Connection
conn String
loc
Just SessionID
buf
| SessionID -> Bool
B.null SessionID
buf -> do
SessionID
chunk <- (ConnectionBackend -> IO SessionID) -> Connection -> IO SessionID
forall a. (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend ConnectionBackend -> IO SessionID
getMoreData Connection
conn
if SessionID -> Bool
B.null SessionID
chunk
then SessionID -> IO (Maybe SessionID, a)
forall {m :: * -> *} {a}. Monad m => SessionID -> m (Maybe a, a)
closeBuf SessionID
chunk
else SessionID -> IO (Maybe SessionID, a)
forall {m :: * -> *}.
Monad m =>
SessionID -> m (Maybe SessionID, a)
updateBuf SessionID
chunk
| Bool
otherwise ->
SessionID -> IO (Maybe SessionID, a)
forall {m :: * -> *}.
Monad m =>
SessionID -> m (Maybe SessionID, a)
updateBuf SessionID
buf
where
getMoreData :: ConnectionBackend -> IO SessionID
getMoreData (ConnectionTLS Context
tlsctx) = Context -> IO SessionID
forall (m :: * -> *). MonadIO m => Context -> m SessionID
TLS.recvData Context
tlsctx
getMoreData (ConnectionSocket Socket
sock) = Socket -> Int -> IO SessionID
N.recv Socket
sock Int
1500
getMoreData (ConnectionStream Handle
h) = Handle -> Int -> IO SessionID
B.hGetSome Handle
h (Int
16 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1024)
updateBuf :: SessionID -> m (Maybe SessionID, a)
updateBuf SessionID
buf = case SessionID -> (a, SessionID)
f SessionID
buf of (a
a, !SessionID
buf') -> (Maybe SessionID, a) -> m (Maybe SessionID, a)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (SessionID -> Maybe SessionID
forall a. a -> Maybe a
Just SessionID
buf', a
a)
closeBuf :: SessionID -> m (Maybe a, a)
closeBuf SessionID
buf = case SessionID -> (a, SessionID)
f SessionID
buf of (a
a, SessionID
_buf') -> (Maybe a, a) -> m (Maybe a, a)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a
forall a. Maybe a
Nothing, a
a)
connectionGetLine :: Int
-> Connection
-> IO ByteString
connectionGetLine :: Int -> Connection -> IO SessionID
connectionGetLine Int
limit Connection
conn = IO Any -> Int -> ([SessionID] -> [SessionID]) -> IO SessionID
forall {t}.
t -> Int -> ([SessionID] -> [SessionID]) -> IO SessionID
more (Connection -> String -> IO Any
forall a. Connection -> String -> IO a
throwEOF Connection
conn String
loc) Int
0 [SessionID] -> [SessionID]
forall a. a -> a
id
where
loc :: String
loc = String
"connectionGetLine"
lineTooLong :: IO a
lineTooLong = LineTooLong -> IO a
forall e a. Exception e => e -> IO a
E.throwIO LineTooLong
LineTooLong
more :: t -> Int -> ([SessionID] -> [SessionID]) -> IO SessionID
more t
eofK !Int
currentSz ![SessionID] -> [SessionID]
dl =
(SessionID -> IO SessionID)
-> (SessionID -> IO SessionID) -> IO SessionID -> IO SessionID
forall r.
(SessionID -> IO r) -> (SessionID -> IO r) -> IO r -> IO r
getChunk (\SessionID
s -> let len :: Int
len = SessionID -> Int
B.length SessionID
s
in if Int
currentSz Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
limit
then IO SessionID
forall {a}. IO a
lineTooLong
else t -> Int -> ([SessionID] -> [SessionID]) -> IO SessionID
more t
eofK (Int
currentSz Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
len) ([SessionID] -> [SessionID]
dl ([SessionID] -> [SessionID])
-> ([SessionID] -> [SessionID]) -> [SessionID] -> [SessionID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SessionID
sSessionID -> [SessionID] -> [SessionID]
forall a. a -> [a] -> [a]
:)))
(\SessionID
s -> ([SessionID] -> [SessionID]) -> IO SessionID
done ([SessionID] -> [SessionID]
dl ([SessionID] -> [SessionID])
-> ([SessionID] -> [SessionID]) -> [SessionID] -> [SessionID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SessionID
sSessionID -> [SessionID] -> [SessionID]
forall a. a -> [a] -> [a]
:)))
(([SessionID] -> [SessionID]) -> IO SessionID
done [SessionID] -> [SessionID]
dl)
done :: ([ByteString] -> [ByteString]) -> IO ByteString
done :: ([SessionID] -> [SessionID]) -> IO SessionID
done [SessionID] -> [SessionID]
dl = SessionID -> IO SessionID
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (SessionID -> IO SessionID) -> SessionID -> IO SessionID
forall a b. (a -> b) -> a -> b
$! [SessionID] -> SessionID
B.concat ([SessionID] -> SessionID) -> [SessionID] -> SessionID
forall a b. (a -> b) -> a -> b
$ [SessionID] -> [SessionID]
dl []
getChunk :: (ByteString -> IO r)
-> (ByteString -> IO r)
-> IO r
-> IO r
getChunk :: forall r.
(SessionID -> IO r) -> (SessionID -> IO r) -> IO r -> IO r
getChunk SessionID -> IO r
moreK SessionID -> IO r
doneK IO r
eofK =
IO (IO r) -> IO r
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO r) -> IO r) -> IO (IO r) -> IO r
forall a b. (a -> b) -> a -> b
$ String
-> Connection -> (SessionID -> (IO r, SessionID)) -> IO (IO r)
forall a.
String -> Connection -> (SessionID -> (a, SessionID)) -> IO a
connectionGetChunkBase String
loc Connection
conn ((SessionID -> (IO r, SessionID)) -> IO (IO r))
-> (SessionID -> (IO r, SessionID)) -> IO (IO r)
forall a b. (a -> b) -> a -> b
$ \SessionID
s ->
if SessionID -> Bool
B.null SessionID
s
then (IO r
eofK, SessionID
B.empty)
else case (Word8 -> Bool) -> SessionID -> (SessionID, SessionID)
B.break (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
10) SessionID
s of
(SessionID
a, SessionID
b)
| SessionID -> Bool
B.null SessionID
b -> (SessionID -> IO r
moreK SessionID
a, SessionID
B.empty)
| Bool
otherwise -> (SessionID -> IO r
doneK SessionID
a, HasCallStack => SessionID -> SessionID
SessionID -> SessionID
B.tail SessionID
b)
throwEOF :: Connection -> String -> IO a
throwEOF :: forall a. Connection -> String -> IO a
throwEOF Connection
conn String
loc =
IOException -> IO a
forall e a. Exception e => e -> IO a
E.throwIO (IOException -> IO a) -> IOException -> IO a
forall a b. (a -> b) -> a -> b
$ IOErrorType
-> String -> Maybe Handle -> Maybe String -> IOException
E.mkIOError IOErrorType
E.eofErrorType String
loc' Maybe Handle
forall a. Maybe a
Nothing (String -> Maybe String
forall a. a -> Maybe a
Just String
path)
where
loc' :: String
loc' = String
"Network.Connection." String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
loc
path :: String
path = let (String
host, PortNumber
port) = Connection -> ConnectionID
connectionID Connection
conn
in String
host String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":" String -> ShowS
forall a. [a] -> [a] -> [a]
++ PortNumber -> String
forall a. Show a => a -> String
show PortNumber
port
connectionClose :: Connection -> IO ()
connectionClose :: Connection -> IO ()
connectionClose = (ConnectionBackend -> IO ()) -> Connection -> IO ()
forall a. (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend ConnectionBackend -> IO ()
backendClose
where backendClose :: ConnectionBackend -> IO ()
backendClose (ConnectionTLS Context
ctx) = IO () -> IO ()
ignoreIOExc (Context -> IO ()
forall (m :: * -> *). MonadIO m => Context -> m ()
TLS.bye Context
ctx) IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`E.finally` Context -> IO ()
TLS.contextClose Context
ctx
backendClose (ConnectionSocket Socket
sock) = Socket -> IO ()
close Socket
sock
backendClose (ConnectionStream Handle
h) = Handle -> IO ()
hClose Handle
h
ignoreIOExc :: IO () -> IO ()
ignoreIOExc IO ()
action = IO ()
action IO () -> (IOException -> IO ()) -> IO ()
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`E.catch` \(IOException
_ :: E.IOException) -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
connectionSetSecure :: ConnectionContext
-> Connection
-> TLSSettings
-> IO ()
connectionSetSecure :: ConnectionContext -> Connection -> TLSSettings -> IO ()
connectionSetSecure ConnectionContext
cg Connection
connection TLSSettings
params =
MVar (Maybe SessionID)
-> (Maybe SessionID -> IO (Maybe SessionID)) -> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ (Connection -> MVar (Maybe SessionID)
connectionBuffer Connection
connection) ((Maybe SessionID -> IO (Maybe SessionID)) -> IO ())
-> (Maybe SessionID -> IO (Maybe SessionID)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Maybe SessionID
b ->
MVar ConnectionBackend
-> (ConnectionBackend -> IO (ConnectionBackend, Maybe SessionID))
-> IO (Maybe SessionID)
forall a b. MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar (Connection -> MVar ConnectionBackend
connectionBackend Connection
connection) ((ConnectionBackend -> IO (ConnectionBackend, Maybe SessionID))
-> IO (Maybe SessionID))
-> (ConnectionBackend -> IO (ConnectionBackend, Maybe SessionID))
-> IO (Maybe SessionID)
forall a b. (a -> b) -> a -> b
$ \ConnectionBackend
backend ->
case ConnectionBackend
backend of
(ConnectionStream Handle
h) -> do Context
ctx <- Handle -> ClientParams -> IO Context
forall backend.
HasBackend backend =>
backend -> ClientParams -> IO Context
tlsEstablish Handle
h (ConnectionContext -> ConnectionID -> TLSSettings -> ClientParams
makeTLSParams ConnectionContext
cg (Connection -> ConnectionID
connectionID Connection
connection) TLSSettings
params)
(ConnectionBackend, Maybe SessionID)
-> IO (ConnectionBackend, Maybe SessionID)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Context -> ConnectionBackend
ConnectionTLS Context
ctx, SessionID -> Maybe SessionID
forall a. a -> Maybe a
Just SessionID
B.empty)
(ConnectionSocket Socket
s) -> do Context
ctx <- Socket -> ClientParams -> IO Context
forall backend.
HasBackend backend =>
backend -> ClientParams -> IO Context
tlsEstablish Socket
s (ConnectionContext -> ConnectionID -> TLSSettings -> ClientParams
makeTLSParams ConnectionContext
cg (Connection -> ConnectionID
connectionID Connection
connection) TLSSettings
params)
(ConnectionBackend, Maybe SessionID)
-> IO (ConnectionBackend, Maybe SessionID)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Context -> ConnectionBackend
ConnectionTLS Context
ctx, SessionID -> Maybe SessionID
forall a. a -> Maybe a
Just SessionID
B.empty)
(ConnectionTLS Context
_) -> (ConnectionBackend, Maybe SessionID)
-> IO (ConnectionBackend, Maybe SessionID)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ConnectionBackend
backend, Maybe SessionID
b)
connectionIsSecure :: Connection -> IO Bool
connectionIsSecure :: Connection -> IO Bool
connectionIsSecure Connection
conn = (ConnectionBackend -> IO Bool) -> Connection -> IO Bool
forall a. (ConnectionBackend -> IO a) -> Connection -> IO a
withBackend ConnectionBackend -> IO Bool
forall {m :: * -> *}. Monad m => ConnectionBackend -> m Bool
isSecure Connection
conn
where isSecure :: ConnectionBackend -> m Bool
isSecure (ConnectionStream Handle
_) = Bool -> m Bool
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
isSecure (ConnectionSocket Socket
_) = Bool -> m Bool
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
isSecure (ConnectionTLS Context
_) = Bool -> m Bool
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
tlsEstablish :: TLS.HasBackend backend => backend -> TLS.ClientParams -> IO TLS.Context
tlsEstablish :: forall backend.
HasBackend backend =>
backend -> ClientParams -> IO Context
tlsEstablish backend
handle ClientParams
tlsParams = do
Context
ctx <- backend -> ClientParams -> IO Context
forall (m :: * -> *) backend params.
(MonadIO m, HasBackend backend, TLSParams params) =>
backend -> params -> m Context
TLS.contextNew backend
handle ClientParams
tlsParams
Context -> IO ()
forall (m :: * -> *). MonadIO m => Context -> m ()
TLS.handshake Context
ctx
Context -> IO Context
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Context
ctx