Jeremy Shaw wrote:

import Control.Concurrent
import Control.Concurrent.MVar
import System.Posix.Types

data RW = Read | Write

threadWaitReadWrite :: Fd -> IO RW
threadWaitReadWrite fd =
  do m <- newEmptyMVar
     rid <- forkIO $ threadWaitRead fd  >> putMVar m Read
     wid <- forkIO $ threadWaitWrite fd >> putMVar m Write
     r <- takeMVar m
     killThread rid
     killThread wid
     return r

[--snip--]

I've tested this extensively during this weekend and not a single "leaked" FD so far.

I think we can safely say that polling an FD for read readiness is sufficient to properly detect a disconnected client regardless of why/how the client disconnected.

The only issue I can see with just dropping the above code directly into the sendfile library is that it may lead to busy-waiting on EAGAIN *if* the client is actually trying to send data to the server while it's receiving the file via sendfile(). If the client sends even a single byte and the server isn't reading it from the socket, then threadWaitRead will keep returning immediately since it's level-triggered rather than edge triggered.

In the worst case this could be exploited by evil clients as a trivial way to DoS a server -- simply send data while the server is sending you a file. Bam, instant 100% CPU utilization on the server.

Not sure what the best solution for this would be, API-wise... Maybe actually have sendfile read the data and supply it to a user-defined function which could react to the data in some way? (Could supply two standard functions: "disconnect immediately" and "accumulate all received data into a bytestring".)

Cheers,

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to