{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_HADDOCK hide, not-home #-}
module Network.Http.Inconvenience (
URL,
modifyContextSSL,
establishConnection,
get,
post,
postForm,
encodedFormBody,
put,
baselineContextSSL,
concatHandler',
jsonBody,
jsonHandler,
TooManyRedirects(..),
HttpClientError(..),
splitURI,
parseURL
) where
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder as Builder (fromByteString, fromLazyByteString,
fromWord8, toByteString)
import qualified Blaze.ByteString.Builder.Char8 as Builder (fromString)
import Control.Exception (Exception, bracket, throw)
import Data.Aeson (FromJSON, ToJSON, Result (..), fromJSON, json', encode)
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)
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
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
import GHC.Word (Word8 (..))
import Network.URI (URI (..), URIAuth (..), isAbsoluteURI,
parseRelativeReference,
parseURI, escapeURIString, isAllowedInURI, 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 qualified System.IO.Streams.Attoparsec 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
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 -> HashSet Char -> Bool) -> HashSet Char -> Char -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Char -> HashSet Char -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
HashSet.member HashSet 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 :: HashSet Char
urlEncodeTable :: HashSet Char
urlEncodeTable = [Char] -> HashSet Char
forall a. (Eq a, Hashable a) => [a] -> HashSet a
HashSet.fromList ([Char] -> HashSet Char) -> [Char] -> HashSet 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'
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
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
#if defined(darwin_HOST_OS)
SSL.contextSetVerificationMode ctx SSL.VerifyNone
#elif defined(mingw32_HOST_OS)
SSL.contextSetVerificationMode ctx SSL.VerifyNone
#elif defined(freebsd_HOST_OS)
SSL.contextSetCAFile ctx "/usr/local/etc/ssl/cert.pem"
SSL.contextSetVerificationMode ctx $ SSL.VerifyPeer True True Nothing
#elif defined(openbsd_HOST_OS)
SSL.contextSetCAFile ctx "/etc/ssl/cert.pem"
SSL.contextSetVerificationMode ctx $ SSL.VerifyPeer True True Nothing
#else
Bool
fedora <- [Char] -> IO Bool
doesDirectoryExist [Char]
"/etc/pki/tls"
if Bool
fedora
then do
SSLContext -> [Char] -> IO ()
SSL.contextSetCAFile SSLContext
ctx [Char]
"/etc/pki/tls/certs/ca-bundle.crt"
else do
SSLContext -> [Char] -> IO ()
SSL.contextSetCADirectory SSLContext
ctx [Char]
"/etc/ssl/certs"
SSLContext -> VerificationMode -> IO ()
SSL.contextSetVerificationMode SSLContext
ctx (VerificationMode -> IO ()) -> VerificationMode -> IO ()
forall a b. (a -> b) -> a -> b
$ Bool
-> Bool
-> Maybe (Bool -> X509StoreCtx -> IO Bool)
-> VerificationMode
SSL.VerifyPeer Bool
True Bool
True Maybe (Bool -> X509StoreCtx -> IO Bool)
forall a. Maybe a
Nothing
#endif
SSLContext -> IO SSLContext
forall (m :: * -> *) a. Monad m => a -> m a
return SSLContext
ctx
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 = (Char -> Bool) -> [Char] -> [Char]
escapeURIString Char -> Bool
isAllowedInURI ([Char] -> [Char]) -> [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ 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
jsonBody :: ToJSON a => a -> OutputStream Builder -> IO ()
jsonBody :: a -> OutputStream Builder -> IO ()
jsonBody a
thing OutputStream Builder
o = do
let b :: Builder
b = ByteString -> Builder
Builder.fromLazyByteString (a -> ByteString
forall a. ToJSON a => a -> ByteString
encode a
thing)
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
jsonHandler
:: (FromJSON α)
=> Response
-> InputStream ByteString
-> IO α
jsonHandler :: Response -> InputStream ByteString -> IO α
jsonHandler Response
_ InputStream ByteString
i = do
Value
v <- Parser Value -> InputStream ByteString -> IO Value
forall r. Parser r -> InputStream ByteString -> IO r
Streams.parseFromStream Parser Value
json' InputStream ByteString
i
let r :: Result α
r = Value -> Result α
forall a. FromJSON a => Value -> Result a
fromJSON Value
v
case Result α
r of
(Success α
a) -> α -> IO α
forall (m :: * -> *) a. Monad m => a -> m a
return α
a
(Error [Char]
str) -> [Char] -> IO α
forall a. HasCallStack => [Char] -> a
error [Char]
str