module Ecluse.Core.Worker.Fetch (
fetchArtifactBytes,
) where
import Network.HTTP.Client (HttpException, Manager, Request, brRead, responseBody, withResponse)
import UnliftIO.Exception (try)
import Ecluse.Core.Registry.Npm (ResponseBoundExceeded (ResponseBoundExceeded))
import Ecluse.Core.Registry.Npm.Request (artifactRequestByUrl)
import Ecluse.Core.Security (Limits (maxBodyBytes), boundedRead, defaultLimits)
import Ecluse.Core.Worker.Types (WorkerM, wrManager)
fetchArtifactBytes :: Text -> WorkerM (Either Text ByteString)
fetchArtifactBytes :: Text -> WorkerM (Either Text ByteString)
fetchArtifactBytes Text
url = do
manager <- (WorkerRuntime -> Manager) -> WorkerM Manager
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks WorkerRuntime -> Manager
wrManager
case artifactRequestByUrl "" Nothing url of
Left UrlFormationError
urlErr -> Either Text ByteString -> WorkerM (Either Text ByteString)
forall a. a -> WorkerM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Either Text ByteString
forall a b. a -> Either a b
Left (Text
"unformable artifact URL: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> UrlFormationError -> Text
forall b a. (Show a, IsString b) => a -> b
show UrlFormationError
urlErr))
Right Request
request ->
WorkerM (Either ResponseBoundExceeded ByteString)
-> WorkerM
(Either HttpException (Either ResponseBoundExceeded ByteString))
forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> m (Either e a)
try (IO (Either ResponseBoundExceeded ByteString)
-> WorkerM (Either ResponseBoundExceeded ByteString)
forall a. IO a -> WorkerM a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Manager -> Request -> IO (Either ResponseBoundExceeded ByteString)
boundedFetch Manager
manager Request
request)) WorkerM
(Either HttpException (Either ResponseBoundExceeded ByteString))
-> (Either HttpException (Either ResponseBoundExceeded ByteString)
-> Either Text ByteString)
-> WorkerM (Either Text ByteString)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
Left (HttpException
e :: HttpException) -> Text -> Either Text ByteString
forall a b. a -> Either a b
Left (Text
"artifact fetch failed: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> HttpException -> Text
forall b a. (Show a, IsString b) => a -> b
show HttpException
e)
Right (Left (ResponseBoundExceeded LimitError
limitErr)) ->
Text -> Either Text ByteString
forall a b. a -> Either a b
Left (Text
"artifact exceeded the response bound: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> LimitError -> Text
forall b a. (Show a, IsString b) => a -> b
show LimitError
limitErr)
Right (Right ByteString
bytes) -> ByteString -> Either Text ByteString
forall a b. b -> Either a b
Right ByteString
bytes
boundedFetch :: Manager -> Request -> IO (Either ResponseBoundExceeded ByteString)
boundedFetch :: Manager -> Request -> IO (Either ResponseBoundExceeded ByteString)
boundedFetch Manager
manager Request
request =
Request
-> Manager
-> (Response BodyReader
-> IO (Either ResponseBoundExceeded ByteString))
-> IO (Either ResponseBoundExceeded ByteString)
forall a.
Request -> Manager -> (Response BodyReader -> IO a) -> IO a
withResponse Request
request Manager
manager ((Response BodyReader
-> IO (Either ResponseBoundExceeded ByteString))
-> IO (Either ResponseBoundExceeded ByteString))
-> (Response BodyReader
-> IO (Either ResponseBoundExceeded ByteString))
-> IO (Either ResponseBoundExceeded ByteString)
forall a b. (a -> b) -> a -> b
$ \Response BodyReader
response ->
Limits -> BodyReader -> IO (Either LimitError ByteString)
forall (m :: * -> *).
Monad m =>
Limits -> m ByteString -> m (Either LimitError ByteString)
boundedRead Limits
workerArtifactLimits (BodyReader -> BodyReader
brRead (Response BodyReader -> BodyReader
forall body. Response body -> body
responseBody Response BodyReader
response)) IO (Either LimitError ByteString)
-> (Either LimitError ByteString
-> IO (Either ResponseBoundExceeded ByteString))
-> IO (Either ResponseBoundExceeded ByteString)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Right ByteString
body -> Either ResponseBoundExceeded ByteString
-> IO (Either ResponseBoundExceeded ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Either ResponseBoundExceeded ByteString
forall a b. b -> Either a b
Right ByteString
body)
Left LimitError
limitErr -> Either ResponseBoundExceeded ByteString
-> IO (Either ResponseBoundExceeded ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ResponseBoundExceeded -> Either ResponseBoundExceeded ByteString
forall a b. a -> Either a b
Left (LimitError -> ResponseBoundExceeded
ResponseBoundExceeded LimitError
limitErr))
workerArtifactLimits :: Limits
workerArtifactLimits :: Limits
workerArtifactLimits = Limits
defaultLimits{maxBodyBytes = 512 * 1024 * 1024}