-
Notifications
You must be signed in to change notification settings - Fork 201
Description
Hi all,
I'd like to discuss and hopefully improve error reporting/handling in network library.
The problem is this:
Wrapper functions(connect, bind, send etc.) hides errors that are normally reported by native versions using return codes and errno.
For example, native connect function reports errors using -1 return value and then users can learn about specifics using errno. However, connect bindings of network package hides all errors from users. There's no way to safely catch errors returned by errno when timeout happens, or when there is a problem with the connection etc.
This is because:
- Bindings/wrappers in
networkusesIO ()which doesn't say anything helpful about possible failures. - Documentation doesn't mention errors.
- We can't extend
IOException(orIOErrorTypeof GHC) type with new constructors for exceptions specific to those functions.
So in a sense, Haskell bindings/wrappers are even worse than native versions in the sense that it's possible to write safe applications using native versions of network functions while that's not possible in Haskell versions.
This is really awful.
What's even worse is that the library actually allows us to set socket properties like SO_RCVTIMEO and SO_SNDTIMEO. So we can set timeouts, but can't handle them.
As a concrete example, let's see this scenario:
I have an application that creates lots of sockets, both TCP and UDP. I want to handle all networking errors, either log them or handle them properly.
While using connect, I want to do something when it fails because of a timeout. However, if it fails because of a connection error, then I want to log it(or show it to the user).
To do this I have to dive into network source code, try to figure out what IOExceptions are thrown when those errors happen, hope that no same IOExceptions are thrown for different types of errors, and then pattern match in my program for that IOExceptions.
This is obviously horrible and unsafe. (and cannot work for some cases, e.g. what happens if network uses same errors for two different errors reported by errno?)
In pseudo-code:
import GHC.IO.Exception
import System.IO.Error
myFun = flip catchIOError errHandler $ do
...
putStrLn "connecting..."
connect sock addr
putStrLn "connected."
...
where
errHandler :: IOError -> IO ()
errHandler IOError{ioe_type=NoSuchThing} = putStrLn "Problems with connection."
errHandler IOError{ioe_type=TimeExpired} = putStrLn "Timeout happened."
errHandler err = putStrLn $ "Unhandled error: " ++ show errAbout #62: I think we shouldn't rely on changes in base to be able to report errors to users properly. We can handle this in network without any extra support from base and I think this is right way to go.
So I suggest having types like this:
connect :: Socket -> SockAddr -> IO (Maybe ErrNo)where ErrNo is errno constants.
I think this is only safe way to go, because that's the type of errno and we can't make the type more specific without making assumptions about error codes returned by network functions.
So, what do you think? Can we improve the library by addressing those for the next version? I'm willing to help with coding part if you like the idea.