{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.HTTP.Client.Core
( withResponse
, httpLbs
, httpNoBody
, httpRaw
, httpRaw'
, getModifiedRequestManager
, responseOpen
, responseClose
, httpRedirect
, httpRedirect'
, withConnection
, handleClosedRead
) where
import Network.HTTP.Types
import Network.HTTP.Client.Manager
import Network.HTTP.Client.Types
import Network.HTTP.Client.Headers
import Network.HTTP.Client.Body
import Network.HTTP.Client.Request
import Network.HTTP.Client.Response
import Network.HTTP.Client.Cookies
import Data.Maybe (fromMaybe, isJust)
import Data.Time
import Control.Exception
import qualified Data.ByteString.Lazy as L
import Data.Monoid
import Control.Monad (void)
import System.Timeout (timeout)
import Data.KeyedPool
import GHC.IO.Exception (IOException(..), IOErrorType(..))
withResponse :: Request
-> Manager
-> (Response BodyReader -> IO a)
-> IO a
withResponse :: forall a.
Request -> Manager -> (Response BodyReader -> IO a) -> IO a
withResponse Request
req Manager
man Response BodyReader -> IO a
f = forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (Request -> Manager -> IO (Response BodyReader)
responseOpen Request
req Manager
man) forall a. Response a -> IO ()
responseClose Response BodyReader -> IO a
f
httpLbs :: Request -> Manager -> IO (Response L.ByteString)
httpLbs :: Request -> Manager -> IO (Response ByteString)
httpLbs Request
req Manager
man = forall a.
Request -> Manager -> (Response BodyReader -> IO a) -> IO a
withResponse Request
req Manager
man forall a b. (a -> b) -> a -> b
$ \Response BodyReader
res -> do
[ByteString]
bss <- BodyReader -> IO [ByteString]
brConsume forall a b. (a -> b) -> a -> b
$ forall body. Response body -> body
responseBody Response BodyReader
res
forall (m :: * -> *) a. Monad m => a -> m a
return Response BodyReader
res { responseBody :: ByteString
responseBody = [ByteString] -> ByteString
L.fromChunks [ByteString]
bss }
httpNoBody :: Request -> Manager -> IO (Response ())
httpNoBody :: Request -> Manager -> IO (Response ())
httpNoBody Request
req Manager
man = forall a.
Request -> Manager -> (Response BodyReader -> IO a) -> IO a
withResponse Request
req Manager
man forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Functor f => f a -> f ()
void
httpRaw
:: Request
-> Manager
-> IO (Response BodyReader)
httpRaw :: Request -> Manager -> IO (Response BodyReader)
httpRaw = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Manager -> IO (Request, Response BodyReader)
httpRaw'
httpRaw'
:: Request
-> Manager
-> IO (Request, Response BodyReader)
httpRaw' :: Request -> Manager -> IO (Request, Response BodyReader)
httpRaw' Request
req0 Manager
m = do
let req' :: Request
req' = Manager -> Request -> Request
mSetProxy Manager
m Request
req0
(Request
req, CookieJar
cookie_jar') <- case Request -> Maybe CookieJar
cookieJar Request
req' of
Just CookieJar
cj -> do
UTCTime
now <- IO UTCTime
getCurrentTime
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Request -> CookieJar -> UTCTime -> (Request, CookieJar)
insertCookiesIntoRequest Request
req' (CookieJar -> UTCTime -> CookieJar
evictExpiredCookies CookieJar
cj UTCTime
now) UTCTime
now
Maybe CookieJar
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return (Request
req', forall a. Monoid a => a
Data.Monoid.mempty)
(Maybe Int
timeout', Managed Connection
mconn) <- forall {a} {resource}.
Integral a =>
Maybe Int
-> IO (Managed resource) -> IO (Maybe a, Managed resource)
getConnectionWrapper
(Request -> Maybe Int
responseTimeout' Request
req)
(Request -> Manager -> IO (Managed Connection)
getConn Request
req Manager
m)
Either SomeException (Response BodyReader)
ex <- forall e a. Exception e => IO a -> IO (Either e a)
try forall a b. (a -> b) -> a -> b
$ do
Maybe (IO ())
cont <- Request -> Connection -> IO (Maybe (IO ()))
requestBuilder (Request -> Request
dropProxyAuthSecure Request
req) (forall resource. Managed resource -> resource
managedResource Managed Connection
mconn)
Maybe MaxHeaderLength
-> Maybe Int
-> Request
-> Managed Connection
-> Maybe (IO ())
-> IO (Response BodyReader)
getResponse (Manager -> Maybe MaxHeaderLength
mMaxHeaderLength Manager
m) Maybe Int
timeout' Request
req Managed Connection
mconn Maybe (IO ())
cont
case Either SomeException (Response BodyReader)
ex of
Left SomeException
e | forall resource. Managed resource -> Bool
managedReused Managed Connection
mconn Bool -> Bool -> Bool
&& Manager -> SomeException -> Bool
mRetryableException Manager
m SomeException
e -> do
forall resource. Managed resource -> Reuse -> IO ()
managedRelease Managed Connection
mconn Reuse
DontReuse
Request -> Manager -> IO (Request, Response BodyReader)
httpRaw' Request
req Manager
m
Left SomeException
e -> do
forall resource. Managed resource -> Reuse -> IO ()
managedRelease Managed Connection
mconn Reuse
DontReuse
forall e a. Exception e => e -> IO a
throwIO SomeException
e
Right Response BodyReader
res -> case Request -> Maybe CookieJar
cookieJar Request
req' of
Just CookieJar
_ -> do
UTCTime
now' <- IO UTCTime
getCurrentTime
let (CookieJar
cookie_jar, Response BodyReader
_) = forall a.
Response a
-> Request -> UTCTime -> CookieJar -> (CookieJar, Response a)
updateCookieJar Response BodyReader
res Request
req UTCTime
now' CookieJar
cookie_jar'
forall (m :: * -> *) a. Monad m => a -> m a
return (Request
req, Response BodyReader
res {responseCookieJar :: CookieJar
responseCookieJar = CookieJar
cookie_jar})
Maybe CookieJar
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return (Request
req, Response BodyReader
res)
where
getConnectionWrapper :: Maybe Int
-> IO (Managed resource) -> IO (Maybe a, Managed resource)
getConnectionWrapper Maybe Int
mtimeout IO (Managed resource)
f =
case Maybe Int
mtimeout of
Maybe Int
Nothing -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((,) forall a. Maybe a
Nothing) IO (Managed resource)
f
Just Int
timeout' -> do
UTCTime
before <- IO UTCTime
getCurrentTime
Maybe (Managed resource)
mres <- forall a. Int -> IO a -> IO (Maybe a)
timeout Int
timeout' IO (Managed resource)
f
case Maybe (Managed resource)
mres of
Maybe (Managed resource)
Nothing -> forall a. HttpExceptionContent -> IO a
throwHttp HttpExceptionContent
ConnectionTimeout
Just Managed resource
mConn -> do
UTCTime
now <- IO UTCTime
getCurrentTime
let timeSpentMicro :: NominalDiffTime
timeSpentMicro = UTCTime -> UTCTime -> NominalDiffTime
diffUTCTime UTCTime
now UTCTime
before forall a. Num a => a -> a -> a
* NominalDiffTime
1000000
remainingTime :: a
remainingTime = forall a b. (RealFrac a, Integral b) => a -> b
round forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
timeout' forall a. Num a => a -> a -> a
- NominalDiffTime
timeSpentMicro
if a
remainingTime forall a. Ord a => a -> a -> Bool
<= a
0
then do
forall resource. Managed resource -> Reuse -> IO ()
managedRelease Managed resource
mConn Reuse
DontReuse
forall a. HttpExceptionContent -> IO a
throwHttp HttpExceptionContent
ConnectionTimeout
else forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> Maybe a
Just a
remainingTime, Managed resource
mConn)
responseTimeout' :: Request -> Maybe Int
responseTimeout' Request
req =
case Request -> ResponseTimeout
responseTimeout Request
req of
ResponseTimeout
ResponseTimeoutDefault ->
case Manager -> ResponseTimeout
mResponseTimeout Manager
m of
ResponseTimeout
ResponseTimeoutDefault -> forall a. a -> Maybe a
Just Int
30000000
ResponseTimeout
ResponseTimeoutNone -> forall a. Maybe a
Nothing
ResponseTimeoutMicro Int
u -> forall a. a -> Maybe a
Just Int
u
ResponseTimeout
ResponseTimeoutNone -> forall a. Maybe a
Nothing
ResponseTimeoutMicro Int
u -> forall a. a -> Maybe a
Just Int
u
getModifiedRequestManager :: Manager -> Request -> IO (Manager, Request)
getModifiedRequestManager :: Manager -> Request -> IO (Manager, Request)
getModifiedRequestManager Manager
manager0 Request
req0 = do
let manager :: Manager
manager = forall a. a -> Maybe a -> a
fromMaybe Manager
manager0 (Request -> Maybe Manager
requestManagerOverride Request
req0)
Request
req <- Manager -> Request -> IO Request
mModifyRequest Manager
manager Request
req0
forall (m :: * -> *) a. Monad m => a -> m a
return (Manager
manager, Request
req)
responseOpen :: Request -> Manager -> IO (Response BodyReader)
responseOpen :: Request -> Manager -> IO (Response BodyReader)
responseOpen Request
inputReq Manager
manager' = do
case RequestHeaders -> HeadersValidationResult
validateHeaders (Request -> RequestHeaders
requestHeaders Request
inputReq) of
HeadersValidationResult
GoodHeaders -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
BadHeaders ByteString
reason -> forall a. HttpExceptionContent -> IO a
throwHttp forall a b. (a -> b) -> a -> b
$ ByteString -> HttpExceptionContent
InvalidRequestHeader ByteString
reason
(Manager
manager, Request
req0) <- Manager -> Request -> IO (Manager, Request)
getModifiedRequestManager Manager
manager' Request
inputReq
forall a. Request -> IO a -> IO a
wrapExc Request
req0 forall a b. (a -> b) -> a -> b
$ Manager -> forall a. Request -> IO a -> IO a
mWrapException Manager
manager Request
req0 forall a b. (a -> b) -> a -> b
$ do
(Request
req, Response BodyReader
res) <- Manager -> Int -> Request -> IO (Request, Response BodyReader)
go Manager
manager (Request -> Int
redirectCount Request
req0) Request
req0
Request -> Request -> Response BodyReader -> IO ()
checkResponse Request
req Request
req Response BodyReader
res
Manager -> Response BodyReader -> IO (Response BodyReader)
mModifyResponse Manager
manager Response BodyReader
res
{ responseBody :: BodyReader
responseBody = forall a. Request -> IO a -> IO a
wrapExc Request
req0 (forall body. Response body -> body
responseBody Response BodyReader
res)
}
where
wrapExc :: Request -> IO a -> IO a
wrapExc :: forall a. Request -> IO a -> IO a
wrapExc Request
req0 = forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => e -> IO a
throwIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> HttpExceptionContentWrapper -> HttpException
toHttpException Request
req0
go :: Manager -> Int -> Request -> IO (Request, Response BodyReader)
go Manager
manager0 Int
count Request
req' = Int
-> (Request -> IO (Response BodyReader, Request, Bool))
-> Request
-> IO (Request, Response BodyReader)
httpRedirect'
Int
count
(\Request
req -> do
(Manager
manager, Request
modReq) <- Manager -> Request -> IO (Manager, Request)
getModifiedRequestManager Manager
manager0 Request
req
(Request
req'', Response BodyReader
res) <- Request -> Manager -> IO (Request, Response BodyReader)
httpRaw' Request
modReq Manager
manager
let mreq :: Maybe Request
mreq = if Request -> Int
redirectCount Request
modReq forall a. Eq a => a -> a -> Bool
== Int
0
then forall a. Maybe a
Nothing
else Request -> RequestHeaders -> CookieJar -> Int -> Maybe Request
getRedirectedRequest Request
req'' (forall body. Response body -> RequestHeaders
responseHeaders Response BodyReader
res) (forall body. Response body -> CookieJar
responseCookieJar Response BodyReader
res) (Status -> Int
statusCode (forall body. Response body -> Status
responseStatus Response BodyReader
res))
forall (m :: * -> *) a. Monad m => a -> m a
return (Response BodyReader
res, forall a. a -> Maybe a -> a
fromMaybe Request
req'' Maybe Request
mreq, forall a. Maybe a -> Bool
isJust Maybe Request
mreq))
Request
req'
httpRedirect
:: Int
-> (Request -> IO (Response BodyReader, Maybe Request))
-> Request
-> IO (Response BodyReader)
httpRedirect :: Int
-> (Request -> IO (Response BodyReader, Maybe Request))
-> Request
-> IO (Response BodyReader)
httpRedirect Int
count0 Request -> IO (Response BodyReader, Maybe Request)
http0 Request
req0 = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ Int
-> (Request -> IO (Response BodyReader, Request, Bool))
-> Request
-> IO (Request, Response BodyReader)
httpRedirect' Int
count0 Request -> IO (Response BodyReader, Request, Bool)
http' Request
req0
where
http' :: Request -> IO (Response BodyReader, Request, Bool)
http' Request
req' = do
(Response BodyReader
res, Maybe Request
mbReq) <- Request -> IO (Response BodyReader, Maybe Request)
http0 Request
req'
forall (m :: * -> *) a. Monad m => a -> m a
return (Response BodyReader
res, forall a. a -> Maybe a -> a
fromMaybe Request
req0 Maybe Request
mbReq, forall a. Maybe a -> Bool
isJust Maybe Request
mbReq)
handleClosedRead :: SomeException -> IO L.ByteString
handleClosedRead :: SomeException -> IO ByteString
handleClosedRead SomeException
se
| Just HttpExceptionContent
ConnectionClosed <- forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap HttpExceptionContentWrapper -> HttpExceptionContent
unHttpExceptionContentWrapper (forall e. Exception e => SomeException -> Maybe e
fromException SomeException
se)
= forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
L.empty
| Just (HttpExceptionRequest Request
_ HttpExceptionContent
ConnectionClosed) <- forall e. Exception e => SomeException -> Maybe e
fromException SomeException
se
= forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
L.empty
| Just (IOError Maybe Handle
_ IOErrorType
ResourceVanished String
_ String
_ Maybe CInt
_ Maybe String
_) <- forall e. Exception e => SomeException -> Maybe e
fromException SomeException
se
= forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
L.empty
| Bool
otherwise
= forall e a. Exception e => e -> IO a
throwIO SomeException
se
httpRedirect'
:: Int
-> (Request -> IO (Response BodyReader, Request, Bool))
-> Request
-> IO (Request, Response BodyReader)
httpRedirect' :: Int
-> (Request -> IO (Response BodyReader, Request, Bool))
-> Request
-> IO (Request, Response BodyReader)
httpRedirect' Int
count0 Request -> IO (Response BodyReader, Request, Bool)
http' Request
req0 = forall {t}.
(Ord t, Num t) =>
t
-> Request
-> [Response ByteString]
-> IO (Request, Response BodyReader)
go Int
count0 Request
req0 []
where
go :: t
-> Request
-> [Response ByteString]
-> IO (Request, Response BodyReader)
go t
count Request
_ [Response ByteString]
ress | t
count forall a. Ord a => a -> a -> Bool
< t
0 = forall a. HttpExceptionContent -> IO a
throwHttp forall a b. (a -> b) -> a -> b
$ [Response ByteString] -> HttpExceptionContent
TooManyRedirects [Response ByteString]
ress
go t
count Request
req' [Response ByteString]
ress = do
(Response BodyReader
res, Request
req, Bool
isRedirect) <- Request -> IO (Response BodyReader, Request, Bool)
http' Request
req'
if Bool
isRedirect then do
let maxFlush :: Int
maxFlush = Int
1024
ByteString
lbs <- BodyReader -> Int -> IO ByteString
brReadSome (forall body. Response body -> body
responseBody Response BodyReader
res) Int
maxFlush
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`Control.Exception.catch` SomeException -> IO ByteString
handleClosedRead
forall a. Response a -> IO ()
responseClose Response BodyReader
res
t
-> Request
-> [Response ByteString]
-> IO (Request, Response BodyReader)
go (t
count forall a. Num a => a -> a -> a
- t
1) Request
req (Response BodyReader
res { responseBody :: ByteString
responseBody = ByteString
lbs }forall a. a -> [a] -> [a]
:[Response ByteString]
ress)
else
forall (m :: * -> *) a. Monad m => a -> m a
return (Request
req, Response BodyReader
res)
responseClose :: Response a -> IO ()
responseClose :: forall a. Response a -> IO ()
responseClose = ResponseClose -> IO ()
runResponseClose forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall body. Response body -> ResponseClose
responseClose'
withConnection :: Request -> Manager -> (Connection -> IO a) -> IO a
withConnection :: forall a. Request -> Manager -> (Connection -> IO a) -> IO a
withConnection Request
origReq Manager
man Connection -> IO a
action = do
Managed Connection
mHttpConn <- Request -> Manager -> IO (Managed Connection)
getConn (Manager -> Request -> Request
mSetProxy Manager
man Request
origReq) Manager
man
Connection -> IO a
action (forall resource. Managed resource -> resource
managedResource Managed Connection
mHttpConn) forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall resource. Managed resource -> IO ()
keepAlive Managed Connection
mHttpConn
forall a b. IO a -> IO b -> IO a
`finally` forall resource. Managed resource -> Reuse -> IO ()
managedRelease Managed Connection
mHttpConn Reuse
DontReuse