hackport-0.5: hackage-security/hackage-security-curl/src/Hackage/Security/Client/Repository/HttpLib/Curl.hs
-- | Implementation of 'HttpClient' using external downloader
module Hackage.Security.Client.Repository.HttpLib.Curl (
withClient
) where
import Control.Monad
import Network.URI
import System.Process
import System.IO
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy.Internal as BS.L
import Hackage.Security.Client.Repository.HttpLib
{-------------------------------------------------------------------------------
Top-level API
-------------------------------------------------------------------------------}
-- | HttpClient using external 'curl' executable
--
-- This is currently just a proof of concept. With a bit of luck we'll be
-- able to reuse most of https://github.com/haskell/cabal/pull/2613 for a
-- more serious implementation. Current TODOs:
--
-- * Set HTTP request headers in 'get'
-- * Get HTTP response headers in 'get'
-- * Support range requests
-- * Deal with proxy settings
-- * Configure logging
--
-- Probably we will want to add a @Browser@ or @Manager@ like abstraction (see
-- @hackage-security-HTTP@ and @hackage-security-http-client@, respectively)
-- in order to allow to change settings as we go.
withClient :: (HttpLib -> IO a) -> IO a
withClient callback = do
callback HttpLib {
httpGet = get
, httpGetRange = undefined -- TODO: support range requests
}
{-------------------------------------------------------------------------------
Implementation of the individual methods
-------------------------------------------------------------------------------}
get :: [HttpRequestHeader] -> URI
-> ([HttpResponseHeader] -> BodyReader -> IO a)
-> IO a
get _httpOpts uri callback = do
(Nothing, Just hOut, Nothing, hProc) <- createProcess curl
callback [] (bodyReader hProc hOut)
where
curl :: CreateProcess
curl = (proc "curl" [show uri]) { std_out = CreatePipe }
{-------------------------------------------------------------------------------
Auxiliary
-------------------------------------------------------------------------------}
-- | Construct a body reader for a process
--
-- TODO: We should also deal with stderr
-- TODO: Perhaps this should be in the main library, alongside bodyReaderFromBS.
bodyReader :: ProcessHandle -> Handle -> BodyReader
bodyReader hProc hOut = do
chunk <- BS.hGetSome hOut BS.L.smallChunkSize
when (BS.null chunk) $ do
_exitCode <- waitForProcess hProc
-- TODO: Check for successful termination
return ()
return chunk