Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I catch a 404 status exception thrown by simpleHttp of Http.Conduit

I'm trying to download all png files contained in an html file. I have trouble catching 404 status exceptions though, instead my program just crashes.

Here is some sample to demonstrate:

import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as L

main = do
    let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"    
    imgData <- (simpleHttp badUrl) `catch` statusExceptionHandler  
    L.writeFile "my.png" imgData

statusExceptionHandler ::  t -> IO L.ByteString
statusExceptionHandler e = (putStrLn "oops") >> (return L.empty)

My "oops" message never prints, instead app crashes with:

StatusCodeException (Status {statusCode = 404, statusMessage = "Not Found"}) [("Content-Type","text/html; charset=UTF-8"),("X-Content-Type-Options","nosniff"),("Date","Fri, 27 Jan 2012 03:10:34 GMT"),("Server","sffe"),("Content-Length","964"),("X-XSS-Protection","1; mode=block")]

What am I doing wrong?

Update:

Following Thoma's advice, I changed my code to the following snippet and now have proper exception handling in place.

main = do
    let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"    
    imgData <- (simpleHttp badUrl) `X.catch` statusExceptionHandler  
    case imgData of x | x == L.empty -> return () 
                      | otherwise    -> L.writeFile "my.png" imgData

statusExceptionHandler ::  HttpException -> IO L.ByteString
statusExceptionHandler (StatusCodeException status headers) = 
    putStr "An error occured during download: "
    >> (putStrLn $ show status)
    >> (return L.empty)
like image 975
Thorsten Lorenz Avatar asked Jan 27 '12 03:01

Thorsten Lorenz


2 Answers

In addition to Thomas's answer, you could tell http-conduit not to throw an exception by overriding the checkStatus record of your Request type.

like image 140
Michael Snoyman Avatar answered Nov 06 '22 19:11

Michael Snoyman


You should probably read the Marlow paper on extensible exceptions. The original catch, exported by Prelude and used in your code snipt, only works for IOError's. The http-conduit code is throwing exceptions of a different type, HttpException to be exact. (there is some dynamic typing going on via the Typeable class, see the paper).

The solution? Use catch from Control.Exception and only catch the error types you want to handle (or SomeException for all of them).

import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as L
import Control.Exception as X

main = do
    let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"
    imgData <- (simpleHttp badUrl) `X.catch` statusExceptionHandler
        L.writeFile "my.png" imgData

statusExceptionHandler ::  SomeException -> IO L.ByteString
statusExceptionHandler e = (putStrLn "oops") >> (return L.empty)
like image 38
Thomas M. DuBuisson Avatar answered Nov 06 '22 19:11

Thomas M. DuBuisson