Skip to content

Commit 455bea9

Browse files
committed
Factor out openURL into T.P.Class.IO.HTTP.
Unexported module.
1 parent 7648494 commit 455bea9

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

src/Text/Pandoc/Class/IO/HTTP.hs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
{- |
5+
Module : Text.Pandoc.Class.IO.HTTP
6+
Copyright : Copyright (C) 2025 John MacFarlane
7+
License : GNU GPL, version 2 or above
8+
9+
Maintainer : John MacFarlane <jgm@berkeley.edu>
10+
Stability : alpha
11+
Portability : portable
12+
13+
HTTP fetching functionality for pandoc.
14+
-}
15+
module Text.Pandoc.Class.IO.HTTP
16+
( openURL
17+
) where
18+
19+
import Network.URI (URI(..), parseURI)
20+
import Data.Text (Text)
21+
import Control.Monad.IO.Class (MonadIO)
22+
import Text.Pandoc.Class.PandocMonad (PandocMonad, extractURIData)
23+
import Text.Pandoc.Error (PandocError (..))
24+
import Text.Pandoc.MIME (MimeType)
25+
import qualified Data.ByteString as B
26+
import qualified Data.Text as T
27+
import Control.Monad.Except (throwError)
28+
#ifdef PANDOC_HTTP_SUPPORT
29+
import Data.ByteString.Lazy (toChunks)
30+
import Control.Monad.IO.Class (liftIO)
31+
import System.Environment (getEnv)
32+
import Data.Default (def)
33+
import Network.Connection (TLSSettings(..))
34+
import qualified Network.TLS as TLS
35+
import qualified Network.TLS.Extra as TLS
36+
import System.X509 (getSystemCertificateStore)
37+
import Network.HTTP.Client
38+
(httpLbs, Manager, responseBody, responseHeaders,
39+
Request(port, host, requestHeaders), parseUrlThrow, newManager, HttpException)
40+
import Network.HTTP.Client.Internal (addProxy)
41+
import Network.HTTP.Client.TLS (mkManagerSettings)
42+
import Network.HTTP.Types.Header ( hContentType )
43+
import Network.Socket (withSocketsDo)
44+
import Text.Pandoc.Class.CommonState (CommonState (..))
45+
import Text.Pandoc.Class.PandocMonad ( getsCommonState, modifyCommonState, report )
46+
import qualified Data.CaseInsensitive as CI
47+
import System.IO.Error
48+
import Text.Pandoc.Logging (LogMessage (..))
49+
import qualified Control.Exception as E
50+
import qualified Text.Pandoc.UTF8 as UTF8
51+
#endif
52+
53+
#ifdef PANDOC_HTTP_SUPPORT
54+
getManager :: (PandocMonad m, MonadIO m) => m Manager
55+
getManager = do
56+
mbManager <- getsCommonState stManager
57+
disableCertificateValidation <- getsCommonState stNoCheckCertificate
58+
case mbManager of
59+
Just manager -> pure manager
60+
Nothing -> do
61+
manager <- liftIO $ do
62+
certificateStore <- getSystemCertificateStore
63+
let tlsSettings = TLSSettings $
64+
(TLS.defaultParamsClient "localhost.localdomain" "80")
65+
{ TLS.clientSupported = def{ TLS.supportedCiphers =
66+
TLS.ciphersuite_default
67+
, TLS.supportedExtendedMainSecret =
68+
TLS.AllowEMS }
69+
, TLS.clientShared = def
70+
{ TLS.sharedCAStore = certificateStore
71+
, TLS.sharedValidationCache =
72+
if disableCertificateValidation
73+
then TLS.ValidationCache
74+
(\_ _ _ -> return TLS.ValidationCachePass)
75+
(\_ _ _ -> return ())
76+
else def
77+
}
78+
}
79+
let tlsManagerSettings = mkManagerSettings tlsSettings Nothing
80+
newManager tlsManagerSettings
81+
modifyCommonState $ \st -> st{ stManager = Just manager }
82+
pure manager
83+
#endif
84+
85+
openURL :: (PandocMonad m, MonadIO m) => Text -> m (B.ByteString, Maybe MimeType)
86+
openURL u
87+
| Just (URI{ uriScheme = "data:",
88+
uriPath = upath }) <- parseURI (T.unpack u)
89+
= pure $ extractURIData upath
90+
#ifdef PANDOC_HTTP_SUPPORT
91+
| otherwise = do
92+
let toReqHeader (n, v) = (CI.mk (UTF8.fromText n), UTF8.fromText v)
93+
customHeaders <- map toReqHeader <$> getsCommonState stRequestHeaders
94+
report $ Fetching u
95+
manager <- getManager
96+
res <- liftIO $ E.try $ withSocketsDo $ do
97+
proxy <- tryIOError (getEnv "http_proxy")
98+
let addProxy' x = case proxy of
99+
Left _ -> return x
100+
Right pr -> parseUrlThrow pr >>= \r ->
101+
return (addProxy (host r) (port r) x)
102+
req <- parseUrlThrow (T.unpack u) >>= addProxy'
103+
let req' = req{requestHeaders = customHeaders ++ requestHeaders req}
104+
resp <- httpLbs req' manager
105+
return (B.concat $ toChunks $ responseBody resp,
106+
UTF8.toText `fmap` lookup hContentType (responseHeaders resp))
107+
108+
case res of
109+
Right r -> return r
110+
Left (e :: HttpException)
111+
-> throwError $ PandocHttpError u (T.pack (show e))
112+
#else
113+
| otherwise =
114+
throwError $ PandocHttpError u "pandoc was compiled without HTTP support"
115+
#endif

0 commit comments

Comments
 (0)