urbit/pkg/hair/lib/Vere/Http/Client.hs

87 lines
2.5 KiB
Haskell
Raw Normal View History

2019-05-17 04:25:58 +03:00
{-
- TODO When making a request, handle the case where the request id is
already in use.
- TODO When canceling a request, don't send Http.Canceled if the
request already finished.
-}
2019-05-16 03:00:10 +03:00
module Vere.Http.Client where
import ClassyPrelude
2019-05-17 03:05:34 +03:00
import Data.Void
2019-05-17 04:25:58 +03:00
import Vere.Http as Http
2019-05-17 03:05:34 +03:00
import Control.Concurrent hiding (newEmptyMVar, putMVar)
2019-05-17 04:25:58 +03:00
import qualified Network.HTTP.Client as H
--------------------------------------------------------------------------------
type ReqId = Word
data Ev = Receive ReqId Http.Event -- %receive
2019-05-16 03:00:10 +03:00
data Eff
2019-05-17 04:25:58 +03:00
= NewReq ReqId Request -- %request
| CancelReq ReqId -- %cancel-request
data State = State
{ sManager :: H.Manager
, sLive :: TVar (Map ReqId ThreadId)
, sChan :: MVar Ev
}
--------------------------------------------------------------------------------
cvtReq :: Request -> H.Request
cvtReq = undefined
cvtRespHeaders :: H.Response a -> ResponseHeader
cvtRespHeaders resp = undefined
2019-05-17 03:05:34 +03:00
2019-05-17 04:25:58 +03:00
--------------------------------------------------------------------------------
2019-05-17 03:05:34 +03:00
initState :: IO State
2019-05-17 04:25:58 +03:00
initState = State <$> H.newManager H.defaultManagerSettings
<*> newTVarIO mempty
<*> newEmptyMVar
2019-05-17 03:05:34 +03:00
2019-05-17 04:25:58 +03:00
emit :: State -> Ev -> IO ()
emit (State _ _ chan) event = putMVar chan event
2019-05-17 03:05:34 +03:00
2019-05-17 04:25:58 +03:00
runEff :: State -> Eff -> IO ()
runEff st@(State _ s _) = \case CancelReq id -> cancelReq st id
NewReq id req -> newReq st id req
2019-05-17 03:05:34 +03:00
2019-05-17 04:25:58 +03:00
newReq :: State -> ReqId -> Request -> IO ()
newReq st id req = do tid <- runReq st id req
atomically $ modifyTVar (sLive st) (insertMap id tid)
2019-05-17 03:05:34 +03:00
2019-05-17 04:25:58 +03:00
cancelReq :: State -> ReqId -> IO ()
cancelReq st id =
2019-05-17 03:05:34 +03:00
join $ atomically $ do
2019-05-17 04:25:58 +03:00
tbl <- readTVar (sLive st)
case lookup id tbl of
Nothing -> pure (pure ())
Just tid -> do
writeTVar (sLive st) (deleteMap id tbl)
pure $ do killThread tid
emit st (Receive id Canceled)
runReq :: State -> ReqId -> Request -> IO ThreadId
runReq st id request =
forkIO $ H.withResponse (cvtReq request) (sManager st) $ \resp -> do
let headers = cvtRespHeaders resp
let getChunk = recv (H.responseBody resp)
let loop = getChunk >>= \case
Just bs -> emit st (Receive id $ Received bs) >> loop
Nothing -> emit st (Receive id Done)
emit st (Receive id $ Started headers)
loop
2019-05-17 03:05:34 +03:00
where
2019-05-17 04:25:58 +03:00
recv :: H.BodyReader -> IO (Maybe ByteString)
recv read = read <&> \case chunk | null chunk -> Nothing
| otherwise -> Just chunk