{-# LANGUAGE ScopedTypeVariables #-}
module Network.HTTP.Client.TLS
( tlsManagerSettings
, mkManagerSettings
, getTlsConnection
) where
import Data.Default.Class
import Network.HTTP.Client
import Network.HTTP.Client.Internal
import Control.Exception
import qualified Network.Connection as NC
import Network.Socket (HostAddress)
import qualified Network.TLS as TLS
import qualified Data.ByteString as S
mkManagerSettings :: NC.TLSSettings
-> Maybe NC.SockSettings
-> ManagerSettings
mkManagerSettings tls sock = defaultManagerSettings
{ managerTlsConnection = getTlsConnection (Just tls) sock
, managerTlsProxyConnection = getTlsProxyConnection tls sock
, managerRawConnection =
case sock of
Nothing -> managerRawConnection defaultManagerSettings
Just _ -> getTlsConnection Nothing sock
, managerRetryableException = \e ->
case () of
()
| ((fromException e)::(Maybe TLS.TLSError))==Just TLS.Error_EOF -> True
| otherwise -> case fromException e of
Just (_ :: IOException) -> True
_ ->
case fromException e of
Just NoResponseDataReceived -> True
Just IncompleteHeaders -> True
_ -> False
, managerWrapIOException =
let wrapper se =
case fromException se of
Just e -> toException $ InternalIOException e
Nothing ->
case fromException se of
Just TLS.Terminated{} -> toException $ TlsException se
_ ->
case fromException se of
Just TLS.HandshakeFailed{} -> toException $ TlsException se
_ ->
case fromException se of
Just TLS.ConnectionNotEstablished -> toException $ TlsException se
_ -> se
in handle $ throwIO . wrapper
}
tlsManagerSettings :: ManagerSettings
tlsManagerSettings = mkManagerSettings def Nothing
getTlsConnection :: Maybe NC.TLSSettings
-> Maybe NC.SockSettings
-> IO (Maybe HostAddress -> String -> Int -> IO Connection)
getTlsConnection tls sock = do
context <- NC.initConnectionContext
return $ \_ha host port -> do
conn <- NC.connectTo context NC.ConnectionParams
{ NC.connectionHostname = host
, NC.connectionPort = fromIntegral port
, NC.connectionUseSecure = tls
, NC.connectionUseSocks = sock
}
convertConnection conn
getTlsProxyConnection
:: NC.TLSSettings
-> Maybe NC.SockSettings
-> IO (S.ByteString -> (Connection -> IO ()) -> String -> Maybe HostAddress -> String -> Int -> IO Connection)
getTlsProxyConnection tls sock = do
context <- NC.initConnectionContext
return $ \connstr checkConn serverName _ha host port -> do
conn <- NC.connectTo context NC.ConnectionParams
{ NC.connectionHostname = serverName
, NC.connectionPort = fromIntegral port
, NC.connectionUseSecure = Nothing
, NC.connectionUseSocks =
case sock of
Just _ -> error "Cannot use SOCKS and TLS proxying together"
Nothing -> Just $ NC.OtherProxy host $ fromIntegral port
}
NC.connectionPut conn connstr
conn' <- convertConnection conn
checkConn conn'
NC.connectionSetSecure context conn tls
return conn'
convertConnection :: NC.Connection -> IO Connection
convertConnection conn = makeConnection
(NC.connectionGetChunk conn)
(NC.connectionPut conn)
(NC.connectionClose conn `Control.Exception.catch` \(_ :: IOException) -> return ())