{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK hide, not-home #-}
module Network.Http.Inconvenience (
URL,
modifyContextSSL,
getContextSSL,
establishConnection,
get,
post,
postForm,
encodedFormBody,
put,
baselineContextSSL,
voidContextSSL,
contextSetCASystemStore,
concatHandler',
TooManyRedirects(..),
HttpClientError(..),
ConnectionAddress(..),
connectionAddressFromURI,
connectionAddressFromURL,
openConnectionAddress,
openConnectionAddress',
openConnectionAddress'',
splitURI
) where
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder as Builder (fromByteString,
fromWord8, toByteString)
import qualified Blaze.ByteString.Builder.Char8 as Builder (fromString)
import Control.Exception (Exception, bracket, throw)
import Control.Monad (when, unless)
import Data.Bits (Bits (..))
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as S
import Data.ByteString.Internal (c2w, w2c)
import Data.Char (intToDigit, toLower, digitToInt, isHexDigit)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.List (intersperse)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Typeable (Typeable)
import Data.Word (Word16)
import GHC.Exts (Int(..),word2Int#, uncheckedShiftRL#)
import GHC.Word (Word8 (..))
import Network.URI (URI (..), URIAuth (..), isAbsoluteURI,
parseRelativeReference, parseURI, uriToString)
import OpenSSL (withOpenSSL)
import OpenSSL.Session (SSLContext)
import qualified OpenSSL.Session as SSL
import System.IO.Streams (InputStream, OutputStream)
import qualified System.IO.Streams as Streams
import System.IO.Unsafe (unsafePerformIO)
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid (Monoid (..), mappend)
#endif
import Network.Http.Connection
import Network.Http.RequestBuilder
import Network.Http.Types
#if defined(linux_HOST_OS) || defined(freebsd_HOST_OS)
import System.Directory (doesDirectoryExist)
#endif
type URL = ByteString
unescBytes :: [Char] -> ByteString
unescBytes :: [Char] -> ByteString
unescBytes = [Char] -> ByteString
S.pack ([Char] -> ByteString)
-> ([Char] -> [Char]) -> [Char] -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [Char]
go
where
go :: [Char] -> [Char]
go [] = []
go (Char
'%':Char
h:Char
l:[Char]
rest)
| Char -> Bool
isHexDigit Char
h, Char -> Bool
isHexDigit Char
l = Int -> Char
forall a. Enum a => Int -> a
toEnum Int
b Char -> [Char] -> [Char]
forall a. a -> [a] -> [a]
: [Char] -> [Char]
go [Char]
rest
where
b :: Int
b = (Int
16 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Char -> Int
digitToInt Char
h) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
digitToInt Char
l
go (Char
c:[Char]
rest) = Char
c Char -> [Char] -> [Char]
forall a. a -> [a] -> [a]
: [Char] -> [Char]
go [Char]
rest
urlEncode :: ByteString -> URL
urlEncode :: ByteString -> ByteString
urlEncode = Builder -> ByteString
Builder.toByteString (Builder -> ByteString)
-> (ByteString -> Builder) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Builder
urlEncodeBuilder
{-# INLINE urlEncode #-}
urlEncodeBuilder :: ByteString -> Builder
urlEncodeBuilder :: ByteString -> Builder
urlEncodeBuilder = Builder -> ByteString -> Builder
go Builder
forall a. Monoid a => a
mempty
where
go :: Builder -> ByteString -> Builder
go !Builder
b !ByteString
s = Builder
-> ((Char, ByteString) -> Builder)
-> Maybe (Char, ByteString)
-> Builder
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Builder
b' (Char, ByteString) -> Builder
esc (ByteString -> Maybe (Char, ByteString)
S.uncons ByteString
y)
where
(ByteString
x,ByteString
y) = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
S.span ((Char -> Set Char -> Bool) -> Set Char -> Char -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Char -> Set Char -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Set Char
urlEncodeTable) ByteString
s
b' :: Builder
b' = Builder
b Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` ByteString -> Builder
Builder.fromByteString ByteString
x
esc :: (Char, ByteString) -> Builder
esc (Char
c,ByteString
r) = let b'' :: Builder
b'' = if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' '
then Builder
b' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Word8 -> Builder
Builder.fromWord8 (Char -> Word8
c2w Char
'+')
else Builder
b' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Char -> Builder
hexd Char
c
in Builder -> ByteString -> Builder
go Builder
b'' ByteString
r
hexd :: Char -> Builder
hexd :: Char -> Builder
hexd Char
c0 = Word8 -> Builder
Builder.fromWord8 (Char -> Word8
c2w Char
'%') Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Word8 -> Builder
Builder.fromWord8 Word8
hi
Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Word8 -> Builder
Builder.fromWord8 Word8
low
where
!c :: Word8
c = Char -> Word8
c2w Char
c0
toDigit :: Int -> Word8
toDigit = Char -> Word8
c2w (Char -> Word8) -> (Int -> Char) -> Int -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Char
intToDigit
!low :: Word8
low = Int -> Word8
toDigit (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Word8 -> Int
forall a. Enum a => a -> Int
fromEnum (Word8 -> Int) -> Word8 -> Int
forall a b. (a -> b) -> a -> b
$ Word8
c Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0xf
!hi :: Word8
hi = Int -> Word8
toDigit (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ (Word8
c Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0xf0) Word8 -> Int -> Int
`shiftr` Int
4
shiftr :: Word8 -> Int -> Int
shiftr (W8# Word#
a#) (I# Int#
b#) = Int# -> Int
I# (Word# -> Int#
word2Int# (Word# -> Int# -> Word#
uncheckedShiftRL# Word#
a# Int#
b#))
urlEncodeTable :: Set Char
urlEncodeTable :: Set Char
urlEncodeTable = [Char] -> Set Char
forall a. Ord a => [a] -> Set a
Set.fromList ([Char] -> Set Char) -> [Char] -> Set Char
forall a b. (a -> b) -> a -> b
$! (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
filter Char -> Bool
f ([Char] -> [Char]) -> [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$! (Word8 -> Char) -> [Word8] -> [Char]
forall a b. (a -> b) -> [a] -> [b]
map Word8 -> Char
w2c [Word8
0..Word8
255]
where
f :: Char -> Bool
f Char
c | Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'A' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'Z' = Bool
True
| Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'a' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'z' = Bool
True
| Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'0' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9' = Bool
True
f Char
c = Char
c Char -> [Char] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ([Char]
"$-_.!~*'(),"::String)
global :: IORef SSLContext
global :: IORef SSLContext
global = IO (IORef SSLContext) -> IORef SSLContext
forall a. IO a -> a
unsafePerformIO (IO (IORef SSLContext) -> IORef SSLContext)
-> IO (IORef SSLContext) -> IORef SSLContext
forall a b. (a -> b) -> a -> b
$ do
SSLContext
ctx <- IO SSLContext
baselineContextSSL
SSLContext -> IO (IORef SSLContext)
forall a. a -> IO (IORef a)
newIORef SSLContext
ctx
{-# NOINLINE global #-}
modifyContextSSL :: (SSLContext -> IO SSLContext) -> IO ()
modifyContextSSL :: (SSLContext -> IO SSLContext) -> IO ()
modifyContextSSL SSLContext -> IO SSLContext
f = do
SSLContext
ctx <- IORef SSLContext -> IO SSLContext
forall a. IORef a -> IO a
readIORef IORef SSLContext
global
SSLContext
ctx' <- SSLContext -> IO SSLContext
f SSLContext
ctx
IORef SSLContext -> SSLContext -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef SSLContext
global SSLContext
ctx'
getContextSSL :: IO SSLContext
getContextSSL :: IO SSLContext
getContextSSL = IORef SSLContext -> IO SSLContext
forall a. IORef a -> IO a
readIORef IORef SSLContext
global
establishConnection :: URL -> IO (Connection)
establishConnection :: ByteString -> IO Connection
establishConnection ByteString
r' = do
URI -> IO Connection
establish URI
u
where
u :: URI
u = ByteString -> URI
parseURL ByteString
r'
{-# INLINE establishConnection #-}
establish :: URI -> IO (Connection)
establish :: URI -> IO Connection
establish URI
u =
case [Char]
scheme of
[Char]
"http:" -> do
ByteString -> Port -> IO Connection
openConnection ByteString
host Port
port
[Char]
"https:" -> do
SSLContext
ctx <- IORef SSLContext -> IO SSLContext
forall a. IORef a -> IO a
readIORef IORef SSLContext
global
SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL SSLContext
ctx ByteString
host Port
ports
[Char]
"unix:" -> do
[Char] -> IO Connection
openConnectionUnix ([Char] -> IO Connection) -> [Char] -> IO Connection
forall a b. (a -> b) -> a -> b
$ URI -> [Char]
uriPath URI
u
[Char]
_ -> [Char] -> IO Connection
forall a. HasCallStack => [Char] -> a
error ([Char]
"Unknown URI scheme " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
scheme)
where
scheme :: [Char]
scheme = URI -> [Char]
uriScheme URI
u
auth :: URIAuth
auth = case URI -> Maybe URIAuth
uriAuthority URI
u of
Just URIAuth
x -> URIAuth
x
Maybe URIAuth
Nothing -> [Char] -> [Char] -> [Char] -> URIAuth
URIAuth [Char]
"" [Char]
"localhost" [Char]
""
host :: ByteString
host = [Char] -> ByteString
S.pack (URIAuth -> [Char]
uriRegName URIAuth
auth)
port :: Port
port = case URIAuth -> [Char]
uriPort URIAuth
auth of
[Char]
"" -> Port
80
[Char]
_ -> [Char] -> Port
forall a. Read a => [Char] -> a
read ([Char] -> Port) -> [Char] -> Port
forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
forall a. [a] -> [a]
tail ([Char] -> [Char]) -> [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ URIAuth -> [Char]
uriPort URIAuth
auth :: Word16
ports :: Port
ports = case URIAuth -> [Char]
uriPort URIAuth
auth of
[Char]
"" -> Port
443
[Char]
_ -> [Char] -> Port
forall a. Read a => [Char] -> a
read ([Char] -> Port) -> [Char] -> Port
forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
forall a. [a] -> [a]
tail ([Char] -> [Char]) -> [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ URIAuth -> [Char]
uriPort URIAuth
auth :: Word16
data ConnectionAddress
= ConnectionAddressHttp !Hostname !Word16
| ConnectionAddressHttps !Hostname !Word16
| ConnectionAddressHttpUnix !ByteString
deriving Int -> ConnectionAddress -> [Char] -> [Char]
[ConnectionAddress] -> [Char] -> [Char]
ConnectionAddress -> [Char]
(Int -> ConnectionAddress -> [Char] -> [Char])
-> (ConnectionAddress -> [Char])
-> ([ConnectionAddress] -> [Char] -> [Char])
-> Show ConnectionAddress
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
showList :: [ConnectionAddress] -> [Char] -> [Char]
$cshowList :: [ConnectionAddress] -> [Char] -> [Char]
show :: ConnectionAddress -> [Char]
$cshow :: ConnectionAddress -> [Char]
showsPrec :: Int -> ConnectionAddress -> [Char] -> [Char]
$cshowsPrec :: Int -> ConnectionAddress -> [Char] -> [Char]
Show
openConnectionAddress :: ConnectionAddress -> IO Connection
openConnectionAddress :: ConnectionAddress -> IO Connection
openConnectionAddress = ((ByteString, Port) -> IO SSLContext)
-> ConnectionAddress -> IO Connection
openConnectionAddress' (IO SSLContext -> (ByteString, Port) -> IO SSLContext
forall a b. a -> b -> a
const (IO SSLContext -> (ByteString, Port) -> IO SSLContext)
-> IO SSLContext -> (ByteString, Port) -> IO SSLContext
forall a b. (a -> b) -> a -> b
$ IORef SSLContext -> IO SSLContext
forall a. IORef a -> IO a
readIORef IORef SSLContext
global)
openConnectionAddress' :: ((Hostname,Word16) -> IO SSLContext) -> ConnectionAddress -> IO Connection
openConnectionAddress' :: ((ByteString, Port) -> IO SSLContext)
-> ConnectionAddress -> IO Connection
openConnectionAddress' (ByteString, Port) -> IO SSLContext
getCtx ConnectionAddress
ca = case ConnectionAddress
ca of
ConnectionAddressHttp ByteString
host Port
port -> ByteString -> Port -> IO Connection
openConnection ByteString
host Port
port
ConnectionAddressHttps ByteString
host Port
ports -> do
SSLContext
ctx <- (ByteString, Port) -> IO SSLContext
getCtx (ByteString
host,Port
ports)
SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL SSLContext
ctx ByteString
host Port
ports
ConnectionAddressHttpUnix ByteString
fp -> do
Connection
c <- [Char] -> IO Connection
openConnectionUnix (ByteString -> [Char]
S.unpack ByteString
fp)
Connection -> IO Connection
forall (m :: * -> *) a. Monad m => a -> m a
return Connection
c { cHost :: ByteString
cHost = ByteString
forall a. Monoid a => a
mempty }
openConnectionAddress'' :: ((Hostname,Word16) -> IO (SSLContext, SSL.SSL -> IO ())) -> ConnectionAddress -> IO Connection
openConnectionAddress'' :: ((ByteString, Port) -> IO (SSLContext, SSL -> IO ()))
-> ConnectionAddress -> IO Connection
openConnectionAddress'' (ByteString, Port) -> IO (SSLContext, SSL -> IO ())
getCtx ConnectionAddress
ca = case ConnectionAddress
ca of
ConnectionAddressHttp ByteString
host Port
port -> ByteString -> Port -> IO Connection
openConnection ByteString
host Port
port
ConnectionAddressHttps ByteString
host Port
ports -> do
(SSLContext
ctx,SSL -> IO ()
modssl) <- (ByteString, Port) -> IO (SSLContext, SSL -> IO ())
getCtx (ByteString
host,Port
ports)
(SSL -> IO ()) -> SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL' SSL -> IO ()
modssl SSLContext
ctx ByteString
host Port
ports
ConnectionAddressHttpUnix ByteString
fp -> do
Connection
c <- [Char] -> IO Connection
openConnectionUnix (ByteString -> [Char]
S.unpack ByteString
fp)
Connection -> IO Connection
forall (m :: * -> *) a. Monad m => a -> m a
return Connection
c { cHost :: ByteString
cHost = ByteString
forall a. Monoid a => a
mempty }
connectionAddressFromURL :: URL -> Either String (ConnectionAddress, String, ByteString, String)
connectionAddressFromURL :: ByteString
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char])
connectionAddressFromURL ByteString
r' = do
Text
r <- (UnicodeException -> Either [Char] Text)
-> (Text -> Either [Char] Text)
-> Either UnicodeException Text
-> Either [Char] Text
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (\UnicodeException
_ -> [Char] -> Either [Char] Text
forall a b. a -> Either a b
Left [Char]
"invalid UTF-8 encoding") Text -> Either [Char] Text
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Either UnicodeException Text
T.decodeUtf8' ByteString
r')
URI
u <- Either [Char] URI
-> (URI -> Either [Char] URI) -> Maybe URI -> Either [Char] URI
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([Char] -> Either [Char] URI
forall a b. a -> Either a b
Left [Char]
"invalid URI syntax") URI -> Either [Char] URI
forall (m :: * -> *) a. Monad m => a -> m a
return ([Char] -> Maybe URI
parseURI (Text -> [Char]
T.unpack Text
r))
URI
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char])
connectionAddressFromURI URI
u
connectionAddressFromURI :: URI -> Either String (ConnectionAddress, String, ByteString, String)
connectionAddressFromURI :: URI
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char])
connectionAddressFromURI URI
u = ((ConnectionAddress, ByteString)
-> (ConnectionAddress, [Char], ByteString, [Char]))
-> Either [Char] (ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ConnectionAddress, ByteString)
-> (ConnectionAddress, [Char], ByteString, [Char])
forall a c. (a, c) -> (a, [Char], c, [Char])
addxinfo (Either [Char] (ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char]))
-> Either [Char] (ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, [Char], ByteString, [Char])
forall a b. (a -> b) -> a -> b
$
case (Char -> Char) -> [Char] -> [Char]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (URI -> [Char]
uriScheme URI
u) of
[Char]
"http:" -> do
[Char]
_ <- Either [Char] [Char]
getUrlPath
(ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, ByteString)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Port -> ConnectionAddress
ConnectionAddressHttp ByteString
host (Port -> Port
port Port
80), ByteString
urlpath)
[Char]
"https:" -> do
[Char]
_ <- Either [Char] [Char]
getUrlPath
(ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, ByteString)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Port -> ConnectionAddress
ConnectionAddressHttps ByteString
host (Port -> Port
port Port
443), ByteString
urlpath)
[Char]
"unix:" -> do
Either [Char] ()
noPort
Either [Char] ()
noHost
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (URI -> [Char]
uriPath URI
u)) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"invalid empty path in unix: URI"
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (URI -> [Char]
uriQuery URI
u)) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"invalid query component in unix: URI"
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (URI -> [Char]
uriFragment URI
u)) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"invalid fragment component in unix: URI"
let rfp :: ByteString
rfp = [Char] -> ByteString
unescBytes (URI -> [Char]
uriPath URI
u)
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString -> Int
S.length ByteString
rfp Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
104) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"unix domain socket path must be at most 104 bytes long"
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Char -> ByteString -> Bool
S.elem Char
'\x00' ByteString
rfp) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"unix domain socket path must not contain NUL bytes"
(ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, ByteString)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> ConnectionAddress
ConnectionAddressHttpUnix ByteString
rfp, ByteString
"")
[Char]
"http+unix:" -> do
Either [Char] ()
noPort
ByteString
fp <- Either [Char] ByteString
getUnixPath
(ConnectionAddress, ByteString)
-> Either [Char] (ConnectionAddress, ByteString)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> ConnectionAddress
ConnectionAddressHttpUnix ByteString
fp, ByteString
urlpath)
[Char]
_ -> [Char] -> Either [Char] (ConnectionAddress, ByteString)
forall a b. a -> Either a b
Left ([Char]
"Unknown URI scheme " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ URI -> [Char]
uriScheme URI
u)
where
addxinfo :: (a, c) -> (a, [Char], c, [Char])
addxinfo (a
ca,c
p) = (a
ca, URIAuth -> [Char]
uriUserInfo URIAuth
auth, c
p, URI -> [Char]
uriFragment URI
u)
auth :: URIAuth
auth = case URI -> Maybe URIAuth
uriAuthority URI
u of
Just URIAuth
x -> URIAuth
x
Maybe URIAuth
Nothing -> [Char] -> [Char] -> [Char] -> URIAuth
URIAuth [Char]
"" [Char]
"" [Char]
""
noPort :: Either [Char] ()
noPort = if [Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (URIAuth -> [Char]
uriPort URIAuth
auth) then () -> Either [Char] ()
forall a b. b -> Either a b
Right () else [Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"invalid port number in URI"
noHost :: Either [Char] ()
noHost = case URI -> Maybe URIAuth
uriAuthority URI
u of
Maybe URIAuth
Nothing -> () -> Either [Char] ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just (URIAuth [Char]
"" [Char]
"" [Char]
"") -> () -> Either [Char] ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just URIAuth
_ -> [Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"invalid host component in uri"
getUrlPath :: Either [Char] [Char]
getUrlPath = case URIAuth -> [Char]
uriRegName URIAuth
auth of
[Char]
"" -> [Char] -> Either [Char] [Char]
forall a b. a -> Either a b
Left [Char]
"missing/empty host component in uri"
[Char]
p -> [Char] -> Either [Char] [Char]
forall a b. b -> Either a b
Right [Char]
p
getUnixPath :: Either [Char] ByteString
getUnixPath = case URIAuth -> [Char]
uriRegName URIAuth
auth of
[Char]
"" -> [Char] -> Either [Char] ByteString
forall a b. a -> Either a b
Left [Char]
"missing/empty host component in uri"
[Char]
fp -> do
let rfp :: ByteString
rfp = [Char] -> ByteString
unescBytes [Char]
fp
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString -> Int
S.length ByteString
rfp Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
104) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"unix domain socket path must be at most 104 bytes long"
Bool -> Either [Char] () -> Either [Char] ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Char -> ByteString -> Bool
S.elem Char
'\x00' ByteString
rfp) (Either [Char] () -> Either [Char] ())
-> Either [Char] () -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$
[Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
"unix domain socket path must not contain NUL bytes"
ByteString -> Either [Char] ByteString
forall a b. b -> Either a b
Right ByteString
rfp
urlpath :: ByteString
urlpath = [Char] -> ByteString
S.pack (URI -> [Char]
uriPath URI
u [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ URI -> [Char]
uriQuery URI
u)
host :: ByteString
host = [Char] -> ByteString
S.pack (URIAuth -> [Char]
uriRegName URIAuth
auth)
port :: Port -> Port
port Port
def = case URIAuth -> [Char]
uriPort URIAuth
auth of
[Char]
"" -> Port
def
[Char]
_ -> [Char] -> Port
forall a. Read a => [Char] -> a
read ([Char] -> Port) -> [Char] -> Port
forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
forall a. [a] -> [a]
tail ([Char] -> [Char]) -> [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ URIAuth -> [Char]
uriPort URIAuth
auth :: Word16
baselineContextSSL :: IO SSLContext
baselineContextSSL :: IO SSLContext
baselineContextSSL = IO SSLContext -> IO SSLContext
forall a. IO a -> IO a
withOpenSSL (IO SSLContext -> IO SSLContext) -> IO SSLContext -> IO SSLContext
forall a b. (a -> b) -> a -> b
$ do
SSLContext
ctx <- IO SSLContext
SSL.context
SSLContext -> IO ()
SSL.contextSetDefaultCiphers SSLContext
ctx
Bool
caSet <- SSLContext -> IO Bool
contextSetCASystemStore SSLContext
ctx
if Bool
caSet
then SSLContext -> VerificationMode -> IO ()
SSL.contextSetVerificationMode SSLContext
ctx (Bool
-> Bool
-> Maybe (Bool -> X509StoreCtx -> IO Bool)
-> VerificationMode
SSL.VerifyPeer Bool
True Bool
True Maybe (Bool -> X509StoreCtx -> IO Bool)
forall a. Maybe a
Nothing)
else SSLContext -> VerificationMode -> IO ()
SSL.contextSetVerificationMode SSLContext
ctx VerificationMode
SSL.VerifyNone
SSLContext -> IO SSLContext
forall (m :: * -> *) a. Monad m => a -> m a
return SSLContext
ctx
voidContextSSL :: IO SSLContext
voidContextSSL :: IO SSLContext
voidContextSSL = do
SSLContext
ctx <- IO SSLContext
SSL.context
SSLContext -> [Char] -> IO ()
SSL.contextSetCiphers SSLContext
ctx [Char]
"HIGH"
SSLContext -> VerificationMode -> IO ()
SSL.contextSetVerificationMode SSLContext
ctx (Bool
-> Bool
-> Maybe (Bool -> X509StoreCtx -> IO Bool)
-> VerificationMode
SSL.VerifyPeer Bool
True Bool
True ((Bool -> X509StoreCtx -> IO Bool)
-> Maybe (Bool -> X509StoreCtx -> IO Bool)
forall a. a -> Maybe a
Just (\Bool
_ X509StoreCtx
_ -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False)))
SSLContext -> IO SSLContext
forall (m :: * -> *) a. Monad m => a -> m a
return SSLContext
ctx
contextSetCASystemStore :: SSLContext -> IO Bool
contextSetCASystemStore :: SSLContext -> IO Bool
contextSetCASystemStore SSLContext
ctx = do
#if defined(darwin_HOST_OS)
return False
#elif defined(mingw32_HOST_OS)
return False
#elif defined(freebsd_HOST_OS)
SSL.contextSetCAFile ctx "/usr/local/etc/ssl/cert.pem"
return True
#elif defined(openbsd_HOST_OS)
SSL.contextSetCAFile ctx "/etc/ssl/cert.pem"
return True
#else
Bool
hasFedoraEtcPkiTls <- [Char] -> IO Bool
doesDirectoryExist [Char]
"/etc/pki/tls"
if Bool
hasFedoraEtcPkiTls
then do
SSLContext -> [Char] -> IO ()
SSL.contextSetCAFile SSLContext
ctx [Char]
"/etc/pki/tls/certs/ca-bundle.crt"
Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
else do
SSLContext -> [Char] -> IO ()
SSL.contextSetCADirectory SSLContext
ctx [Char]
"/etc/ssl/certs"
Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
#endif
parseURL :: URL -> URI
parseURL :: ByteString -> URI
parseURL ByteString
r' =
case [Char] -> Maybe URI
parseURI [Char]
r of
Just URI
u -> URI
u
Maybe URI
Nothing -> [Char] -> URI
forall a. HasCallStack => [Char] -> a
error ([Char]
"Can't parse URI " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
r)
where
r :: [Char]
r = Text -> [Char]
T.unpack (Text -> [Char]) -> Text -> [Char]
forall a b. (a -> b) -> a -> b
$ ByteString -> Text
T.decodeUtf8 ByteString
r'
path :: URI -> ByteString
path :: URI -> ByteString
path URI
u = case ByteString
url of
ByteString
"" -> ByteString
"/"
ByteString
_ -> ByteString
url
where
url :: ByteString
url = Text -> ByteString
T.encodeUtf8 (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$! [Char] -> Text
T.pack
([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$! [[Char]] -> [Char]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [URI -> [Char]
uriPath URI
u, URI -> [Char]
uriQuery URI
u, URI -> [Char]
uriFragment URI
u]
get :: URL
-> (Response -> InputStream ByteString -> IO β)
-> IO β
get :: ByteString -> (Response -> InputStream ByteString -> IO β) -> IO β
get ByteString
r' Response -> InputStream ByteString -> IO β
handler = Int
-> ByteString
-> (Response -> InputStream ByteString -> IO β)
-> IO β
forall c.
Int
-> ByteString
-> (Response -> InputStream ByteString -> IO c)
-> IO c
getN Int
0 ByteString
r' Response -> InputStream ByteString -> IO β
handler
getN :: Int
-> ByteString
-> (Response -> InputStream ByteString -> IO c)
-> IO c
getN Int
n ByteString
r' Response -> InputStream ByteString -> IO c
handler = do
IO Connection
-> (Connection -> IO ()) -> (Connection -> IO c) -> IO c
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
(URI -> IO Connection
establish URI
u)
(Connection -> IO ()
teardown)
(Connection -> IO c
process)
where
teardown :: Connection -> IO ()
teardown = Connection -> IO ()
closeConnection
u :: URI
u = ByteString -> URI
parseURL ByteString
r'
q :: Request
q = RequestBuilder () -> Request
forall α. RequestBuilder α -> Request
buildRequest1 (RequestBuilder () -> Request) -> RequestBuilder () -> Request
forall a b. (a -> b) -> a -> b
$ do
Method -> ByteString -> RequestBuilder ()
http Method
GET (URI -> ByteString
path URI
u)
ByteString -> RequestBuilder ()
setAccept ByteString
"*/*"
process :: Connection -> IO c
process Connection
c = do
Connection -> Request -> (OutputStream Builder -> IO ()) -> IO ()
forall α.
Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest Connection
c Request
q OutputStream Builder -> IO ()
emptyBody
Connection -> (Response -> InputStream ByteString -> IO c) -> IO c
forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse Connection
c (URI
-> Int
-> (Response -> InputStream ByteString -> IO c)
-> Response
-> InputStream ByteString
-> IO c
forall β.
URI
-> Int
-> (Response -> InputStream ByteString -> IO β)
-> Response
-> InputStream ByteString
-> IO β
wrapRedirect URI
u Int
n Response -> InputStream ByteString -> IO c
handler)
wrapRedirect
:: URI
-> Int
-> (Response -> InputStream ByteString -> IO β)
-> Response
-> InputStream ByteString
-> IO β
wrapRedirect :: URI
-> Int
-> (Response -> InputStream ByteString -> IO β)
-> Response
-> InputStream ByteString
-> IO β
wrapRedirect URI
u Int
n Response -> InputStream ByteString -> IO β
handler Response
p InputStream ByteString
i = do
if (Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
301 Bool -> Bool -> Bool
|| Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
302 Bool -> Bool -> Bool
|| Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
303 Bool -> Bool -> Bool
|| Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
307)
then case Maybe ByteString
lm of
Just ByteString
l -> Int
-> ByteString
-> (Response -> InputStream ByteString -> IO β)
-> IO β
forall c.
Int
-> ByteString
-> (Response -> InputStream ByteString -> IO c)
-> IO c
getN Int
n' (URI -> ByteString -> ByteString
splitURI URI
u ByteString
l) Response -> InputStream ByteString -> IO β
handler
Maybe ByteString
Nothing -> Response -> InputStream ByteString -> IO β
handler Response
p InputStream ByteString
i
else Response -> InputStream ByteString -> IO β
handler Response
p InputStream ByteString
i
where
s :: Int
s = Response -> Int
getStatusCode Response
p
lm :: Maybe ByteString
lm = Response -> ByteString -> Maybe ByteString
getHeader Response
p ByteString
"Location"
!n' :: Int
n' = if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
5
then Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
else TooManyRedirects -> Int
forall a e. Exception e => e -> a
throw (TooManyRedirects -> Int) -> TooManyRedirects -> Int
forall a b. (a -> b) -> a -> b
$! Int -> TooManyRedirects
TooManyRedirects Int
n
splitURI :: URI -> URL -> URL
splitURI :: URI -> ByteString -> ByteString
splitURI URI
old ByteString
new' =
let
new :: [Char]
new = ByteString -> [Char]
S.unpack ByteString
new'
in
if [Char] -> Bool
isAbsoluteURI [Char]
new
then
ByteString
new'
else
let
rel :: Maybe URI
rel = [Char] -> Maybe URI
parseRelativeReference [Char]
new
in
case Maybe URI
rel of
Maybe URI
Nothing -> ByteString
new'
Just URI
x -> [Char] -> ByteString
S.pack ([Char] -> ByteString) -> [Char] -> ByteString
forall a b. (a -> b) -> a -> b
$ ([Char] -> [Char]) -> URI -> [Char] -> [Char]
uriToString [Char] -> [Char]
forall a. a -> a
id URI
old {
uriPath :: [Char]
uriPath = URI -> [Char]
uriPath URI
x,
uriQuery :: [Char]
uriQuery = URI -> [Char]
uriQuery URI
x,
uriFragment :: [Char]
uriFragment = URI -> [Char]
uriFragment URI
x
} [Char]
""
data TooManyRedirects = TooManyRedirects Int
deriving (Typeable, Int -> TooManyRedirects -> [Char] -> [Char]
[TooManyRedirects] -> [Char] -> [Char]
TooManyRedirects -> [Char]
(Int -> TooManyRedirects -> [Char] -> [Char])
-> (TooManyRedirects -> [Char])
-> ([TooManyRedirects] -> [Char] -> [Char])
-> Show TooManyRedirects
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
showList :: [TooManyRedirects] -> [Char] -> [Char]
$cshowList :: [TooManyRedirects] -> [Char] -> [Char]
show :: TooManyRedirects -> [Char]
$cshow :: TooManyRedirects -> [Char]
showsPrec :: Int -> TooManyRedirects -> [Char] -> [Char]
$cshowsPrec :: Int -> TooManyRedirects -> [Char] -> [Char]
Show, TooManyRedirects -> TooManyRedirects -> Bool
(TooManyRedirects -> TooManyRedirects -> Bool)
-> (TooManyRedirects -> TooManyRedirects -> Bool)
-> Eq TooManyRedirects
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: TooManyRedirects -> TooManyRedirects -> Bool
$c/= :: TooManyRedirects -> TooManyRedirects -> Bool
== :: TooManyRedirects -> TooManyRedirects -> Bool
$c== :: TooManyRedirects -> TooManyRedirects -> Bool
Eq)
instance Exception TooManyRedirects
post :: URL
-> ContentType
-> (OutputStream Builder -> IO α)
-> (Response -> InputStream ByteString -> IO β)
-> IO β
post :: ByteString
-> ByteString
-> (OutputStream Builder -> IO α)
-> (Response -> InputStream ByteString -> IO β)
-> IO β
post ByteString
r' ByteString
t OutputStream Builder -> IO α
body Response -> InputStream ByteString -> IO β
handler = do
IO Connection
-> (Connection -> IO ()) -> (Connection -> IO β) -> IO β
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
(URI -> IO Connection
establish URI
u)
(Connection -> IO ()
teardown)
(Connection -> IO β
process)
where
teardown :: Connection -> IO ()
teardown = Connection -> IO ()
closeConnection
u :: URI
u = ByteString -> URI
parseURL ByteString
r'
q :: Request
q = RequestBuilder () -> Request
forall α. RequestBuilder α -> Request
buildRequest1 (RequestBuilder () -> Request) -> RequestBuilder () -> Request
forall a b. (a -> b) -> a -> b
$ do
Method -> ByteString -> RequestBuilder ()
http Method
POST (URI -> ByteString
path URI
u)
ByteString -> RequestBuilder ()
setAccept ByteString
"*/*"
ByteString -> RequestBuilder ()
setContentType ByteString
t
process :: Connection -> IO β
process Connection
c = do
α
_ <- Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
forall α.
Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest Connection
c Request
q OutputStream Builder -> IO α
body
β
x <- Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse Connection
c Response -> InputStream ByteString -> IO β
handler
β -> IO β
forall (m :: * -> *) a. Monad m => a -> m a
return β
x
postForm
:: URL
-> [(ByteString, ByteString)]
-> (Response -> InputStream ByteString -> IO β)
-> IO β
postForm :: ByteString
-> [(ByteString, ByteString)]
-> (Response -> InputStream ByteString -> IO β)
-> IO β
postForm ByteString
r' [(ByteString, ByteString)]
nvs Response -> InputStream ByteString -> IO β
handler = do
IO Connection
-> (Connection -> IO ()) -> (Connection -> IO β) -> IO β
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
(URI -> IO Connection
establish URI
u)
(Connection -> IO ()
teardown)
(Connection -> IO β
process)
where
teardown :: Connection -> IO ()
teardown = Connection -> IO ()
closeConnection
u :: URI
u = ByteString -> URI
parseURL ByteString
r'
q :: Request
q = RequestBuilder () -> Request
forall α. RequestBuilder α -> Request
buildRequest1 (RequestBuilder () -> Request) -> RequestBuilder () -> Request
forall a b. (a -> b) -> a -> b
$ do
Method -> ByteString -> RequestBuilder ()
http Method
POST (URI -> ByteString
path URI
u)
ByteString -> RequestBuilder ()
setAccept ByteString
"*/*"
ByteString -> RequestBuilder ()
setContentType ByteString
"application/x-www-form-urlencoded"
process :: Connection -> IO β
process Connection
c = do
()
_ <- Connection -> Request -> (OutputStream Builder -> IO ()) -> IO ()
forall α.
Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest Connection
c Request
q ([(ByteString, ByteString)] -> OutputStream Builder -> IO ()
encodedFormBody [(ByteString, ByteString)]
nvs)
β
x <- Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse Connection
c Response -> InputStream ByteString -> IO β
handler
β -> IO β
forall (m :: * -> *) a. Monad m => a -> m a
return β
x
encodedFormBody :: [(ByteString,ByteString)] -> OutputStream Builder -> IO ()
encodedFormBody :: [(ByteString, ByteString)] -> OutputStream Builder -> IO ()
encodedFormBody [(ByteString, ByteString)]
nvs OutputStream Builder
o = do
Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just Builder
b) OutputStream Builder
o
where
b :: Builder
b = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> [Builder] -> Builder
forall a b. (a -> b) -> a -> b
$ Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
intersperse ([Char] -> Builder
Builder.fromString [Char]
"&") ([Builder] -> [Builder]) -> [Builder] -> [Builder]
forall a b. (a -> b) -> a -> b
$ ((ByteString, ByteString) -> Builder)
-> [(ByteString, ByteString)] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString, ByteString) -> Builder
combine [(ByteString, ByteString)]
nvs
combine :: (ByteString,ByteString) -> Builder
combine :: (ByteString, ByteString) -> Builder
combine (ByteString
n',ByteString
v') = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat [ByteString -> Builder
urlEncodeBuilder ByteString
n', [Char] -> Builder
Builder.fromString [Char]
"=", ByteString -> Builder
urlEncodeBuilder ByteString
v']
put :: URL
-> ContentType
-> (OutputStream Builder -> IO α)
-> (Response -> InputStream ByteString -> IO β)
-> IO β
put :: ByteString
-> ByteString
-> (OutputStream Builder -> IO α)
-> (Response -> InputStream ByteString -> IO β)
-> IO β
put ByteString
r' ByteString
t OutputStream Builder -> IO α
body Response -> InputStream ByteString -> IO β
handler = do
IO Connection
-> (Connection -> IO ()) -> (Connection -> IO β) -> IO β
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
(URI -> IO Connection
establish URI
u)
(Connection -> IO ()
teardown)
(Connection -> IO β
process)
where
teardown :: Connection -> IO ()
teardown = Connection -> IO ()
closeConnection
u :: URI
u = ByteString -> URI
parseURL ByteString
r'
q :: Request
q = RequestBuilder () -> Request
forall α. RequestBuilder α -> Request
buildRequest1 (RequestBuilder () -> Request) -> RequestBuilder () -> Request
forall a b. (a -> b) -> a -> b
$ do
Method -> ByteString -> RequestBuilder ()
http Method
PUT (URI -> ByteString
path URI
u)
ByteString -> RequestBuilder ()
setAccept ByteString
"*/*"
ByteString -> ByteString -> RequestBuilder ()
setHeader ByteString
"Content-Type" ByteString
t
process :: Connection -> IO β
process Connection
c = do
α
_ <- Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
forall α.
Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest Connection
c Request
q OutputStream Builder -> IO α
body
β
x <- Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse Connection
c Response -> InputStream ByteString -> IO β
handler
β -> IO β
forall (m :: * -> *) a. Monad m => a -> m a
return β
x
concatHandler' :: Response -> InputStream ByteString -> IO ByteString
concatHandler' :: Response -> InputStream ByteString -> IO ByteString
concatHandler' Response
p InputStream ByteString
i =
if Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
300
then HttpClientError -> IO ByteString
forall a e. Exception e => e -> a
throw (Int -> ByteString -> HttpClientError
HttpClientError Int
s ByteString
m)
else Response -> InputStream ByteString -> IO ByteString
concatHandler Response
p InputStream ByteString
i
where
s :: Int
s = Response -> Int
getStatusCode Response
p
m :: ByteString
m = Response -> ByteString
getStatusMessage Response
p
data HttpClientError = HttpClientError Int ByteString
deriving (Typeable)
instance Exception HttpClientError
instance Show HttpClientError where
show :: HttpClientError -> [Char]
show (HttpClientError Int
s ByteString
msg) = Int -> [Char]
forall a. Show a => a -> [Char]
Prelude.show Int
s [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ ByteString -> [Char]
S.unpack ByteString
msg