Handling 404 via Maybe in http-conduit
-
21-12-2019 - |
Question
Using http-conduit
I want to download a HTTP URL implementing the following semantics (resulting in IO (Maybe LB.ByteString)
):
- If the HTTP response code is 2xx, return
Just
the response body - If the HTTP response code is 404, return
Nothing
- If the response code indicates a redirect, follow according to standard http-conduit settings
- For any other response code, throw a
StatusCodeException
.
How can I do that using httpLbs
without any libraries beyond http-conduit
and its dependencies?
Note: This question was answered in Q&A form and therefore intentionally does not show research effort.
Solution
This is possible by using a custom checkStatus
similar to one of the offical examples.
We will declare checkStatus200
which passes if the response status code is either 2xx or 404. If it doesn't pass, it calls the default checkStatus
function to throw the appropriate exception.
After calling httpLbs
with a Request
using checkStatus200
, we can check the status code and return either Just
the response code or Nothing
.
import Data.Conduit.Binary (sinkFile)
import Network.HTTP.Types.Status (Status(..))
import Network.HTTP.Conduit
import qualified Data.Conduit as C
import Network
import Data.Default (def)
import qualified Data.ByteString.Lazy as LB
-- | @checkStatus@ implementation that accepts
-- 2xx status codes and 404. Calls default implementation
-- on other status codes (i.e. throws exception)
checkStatus200 st@(Status sc _) rh cj =
if (200 <= sc && sc < 300) || sc == 404
then Nothing
else (checkStatus def) st rh cj
-- | Download a HTTP link, returning @Nothing@ on 404 status code
downloadCatch404 :: String
-> IO (Maybe LB.ByteString)
downloadCatch404 url = withSocketsDo $ do
request <- parseUrl url
let request' = request { checkStatus = checkStatus200 }
res <- withManager $ httpLbs request'
let status = statusCode . responseStatus $ res
-- Return Nothing if status code == 404
return $ if status == 404
then Nothing
else Just $ responseBody res