diff options
| author | John MacFarlane <[email protected]> | 2025-12-24 12:17:35 -0700 |
|---|---|---|
| committer | John MacFarlane <[email protected]> | 2025-12-24 12:17:35 -0700 |
| commit | 455bea907d48ff84286015ea6bf8315d34749039 (patch) | |
| tree | 1237d5c6ac9db72dbae5354daca761f784b31486 /src/Text/Pandoc/Class/IO/HTTP.hs | |
| parent | 7648494849e6d3d88f427d1067384e075693b031 (diff) | |
Factor out openURL into T.P.Class.IO.HTTP.
Unexported module.
Diffstat (limited to 'src/Text/Pandoc/Class/IO/HTTP.hs')
| -rw-r--r-- | src/Text/Pandoc/Class/IO/HTTP.hs | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/Text/Pandoc/Class/IO/HTTP.hs b/src/Text/Pandoc/Class/IO/HTTP.hs new file mode 100644 index 000000000..cdb8211a6 --- /dev/null +++ b/src/Text/Pandoc/Class/IO/HTTP.hs @@ -0,0 +1,115 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} +{- | +Module : Text.Pandoc.Class.IO.HTTP +Copyright : Copyright (C) 2025 John MacFarlane +License : GNU GPL, version 2 or above + +Maintainer : John MacFarlane <[email protected]> +Stability : alpha +Portability : portable + +HTTP fetching functionality for pandoc. +-} +module Text.Pandoc.Class.IO.HTTP + ( openURL + ) where + +import Network.URI (URI(..), parseURI) +import Data.Text (Text) +import Control.Monad.IO.Class (MonadIO) +import Text.Pandoc.Class.PandocMonad (PandocMonad, extractURIData) +import Text.Pandoc.Error (PandocError (..)) +import Text.Pandoc.MIME (MimeType) +import qualified Data.ByteString as B +import qualified Data.Text as T +import Control.Monad.Except (throwError) +#ifdef PANDOC_HTTP_SUPPORT +import Data.ByteString.Lazy (toChunks) +import Control.Monad.IO.Class (liftIO) +import System.Environment (getEnv) +import Data.Default (def) +import Network.Connection (TLSSettings(..)) +import qualified Network.TLS as TLS +import qualified Network.TLS.Extra as TLS +import System.X509 (getSystemCertificateStore) +import Network.HTTP.Client + (httpLbs, Manager, responseBody, responseHeaders, + Request(port, host, requestHeaders), parseUrlThrow, newManager, HttpException) +import Network.HTTP.Client.Internal (addProxy) +import Network.HTTP.Client.TLS (mkManagerSettings) +import Network.HTTP.Types.Header ( hContentType ) +import Network.Socket (withSocketsDo) +import Text.Pandoc.Class.CommonState (CommonState (..)) +import Text.Pandoc.Class.PandocMonad ( getsCommonState, modifyCommonState, report ) +import qualified Data.CaseInsensitive as CI +import System.IO.Error +import Text.Pandoc.Logging (LogMessage (..)) +import qualified Control.Exception as E +import qualified Text.Pandoc.UTF8 as UTF8 +#endif + +#ifdef PANDOC_HTTP_SUPPORT +getManager :: (PandocMonad m, MonadIO m) => m Manager +getManager = do + mbManager <- getsCommonState stManager + disableCertificateValidation <- getsCommonState stNoCheckCertificate + case mbManager of + Just manager -> pure manager + Nothing -> do + manager <- liftIO $ do + certificateStore <- getSystemCertificateStore + let tlsSettings = TLSSettings $ + (TLS.defaultParamsClient "localhost.localdomain" "80") + { TLS.clientSupported = def{ TLS.supportedCiphers = + TLS.ciphersuite_default + , TLS.supportedExtendedMainSecret = + TLS.AllowEMS } + , TLS.clientShared = def + { TLS.sharedCAStore = certificateStore + , TLS.sharedValidationCache = + if disableCertificateValidation + then TLS.ValidationCache + (\_ _ _ -> return TLS.ValidationCachePass) + (\_ _ _ -> return ()) + else def + } + } + let tlsManagerSettings = mkManagerSettings tlsSettings Nothing + newManager tlsManagerSettings + modifyCommonState $ \st -> st{ stManager = Just manager } + pure manager +#endif + +openURL :: (PandocMonad m, MonadIO m) => Text -> m (B.ByteString, Maybe MimeType) +openURL u + | Just (URI{ uriScheme = "data:", + uriPath = upath }) <- parseURI (T.unpack u) + = pure $ extractURIData upath +#ifdef PANDOC_HTTP_SUPPORT + | otherwise = do + let toReqHeader (n, v) = (CI.mk (UTF8.fromText n), UTF8.fromText v) + customHeaders <- map toReqHeader <$> getsCommonState stRequestHeaders + report $ Fetching u + manager <- getManager + res <- liftIO $ E.try $ withSocketsDo $ do + proxy <- tryIOError (getEnv "http_proxy") + let addProxy' x = case proxy of + Left _ -> return x + Right pr -> parseUrlThrow pr >>= \r -> + return (addProxy (host r) (port r) x) + req <- parseUrlThrow (T.unpack u) >>= addProxy' + let req' = req{requestHeaders = customHeaders ++ requestHeaders req} + resp <- httpLbs req' manager + return (B.concat $ toChunks $ responseBody resp, + UTF8.toText `fmap` lookup hContentType (responseHeaders resp)) + + case res of + Right r -> return r + Left (e :: HttpException) + -> throwError $ PandocHttpError u (T.pack (show e)) +#else + | otherwise = + throwError $ PandocHttpError u "pandoc was compiled without HTTP support" +#endif |
