module Happstack.Server.Internal.Listen(listen, listen',listenOn) where
import Happstack.Server.Internal.Types (Conf(..), Request, Response)
import Happstack.Server.Internal.Handler (request)
import Happstack.Server.Internal.Socket (acceptLite)
import Happstack.Server.Internal.TimeoutManager (cancel, initialize, register)
import Control.Exception.Extensible as E
import Control.Concurrent (forkIO, killThread, myThreadId)
import Control.Monad (forever, when)
import Network.BSD (getProtocolNumber)
import Network (sClose, Socket)
import Network.Socket as Socket (SocketOption(KeepAlive), setSocketOption,
socket, Family(..), SockAddr,
SocketOption(..), SockAddr(..),
iNADDR_ANY, maxListenQueue, SocketType(..),
bindSocket)
import qualified Network.Socket as Socket (listen)
import System.IO.Error (isFullError)
import System.Posix.Signals
import System.Log.Logger (Priority(..), logM)
log':: Priority -> String -> IO ()
log' = logM "Happstack.Server.HTTP.Listen"
listenOn :: Int -> IO Socket
listenOn portm = do
proto <- getProtocolNumber "tcp"
E.bracketOnError
(socket AF_INET Stream proto)
(sClose)
(\sock -> do
setSocketOption sock ReuseAddr 1
bindSocket sock (SockAddrInet (fromIntegral portm) iNADDR_ANY)
Socket.listen sock (max 1024 maxListenQueue)
return sock
)
listen :: Conf -> (Request -> IO Response) -> IO ()
listen conf hand = do
let port' = port conf
socketm <- listenOn port'
setSocketOption socketm KeepAlive 1
listen' socketm conf hand
listen' :: Socket -> Conf -> (Request -> IO Response) -> IO ()
listen' s conf hand = do
installHandler openEndedPipe Ignore Nothing
let port' = port conf
log' NOTICE ("Listening on port " ++ show port')
tm <- initialize ((timeout conf) * (10^(6 :: Int)))
let eh (x::SomeException) = when ((fromException x) /= Just ThreadKilled) $ log' ERROR ("HTTP request failed with: " ++ show x)
work (sock, hn, p) =
do tid <- myThreadId
thandle <- register tm (killThread tid)
request thandle conf sock (hn,fromIntegral p) hand `E.catch` eh
cancel thandle
sClose sock
loop = forever $ do w <- acceptLite s
forkIO $ work w
pe e = log' ERROR ("ERROR in accept thread: " ++ show e)
infi = loop `catchSome` pe >> infi
infi `finally` (sClose s)
installHandler openEndedPipe Ignore Nothing
return ()
where
catchSome op h = op `E.catches` [
Handler $ \(e :: ArithException) -> h (toException e),
Handler $ \(e :: ArrayException) -> h (toException e),
Handler $ \(e :: IOException) ->
if isFullError e
then return ()
else throw e
]