module Network.HTTP.Lucu.Httpd
( FallbackHandler
, runHttpd
)
where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.Maybe
import Network.BSD
import Network.Socket
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.Interaction
import Network.HTTP.Lucu.RequestReader
import Network.HTTP.Lucu.Resource.Tree
import Network.HTTP.Lucu.ResponseWriter
import Network.HTTP.Lucu.SocketLike as SL
import System.Posix.Signals
runHttpd :: Config -> ResTree -> [FallbackHandler] -> IO ()
runHttpd cnf tree fbs
= withSocketsDo $
do _ <- installHandler sigPIPE Ignore Nothing
let launchers
= catMaybes
[ do scnf <- cnfSSLConfig cnf
addr <- cnfServerV4Addr cnf
return ( do so <- listenOn AF_INET addr (sslServerPort scnf)
launchListener (sslContext scnf, so)
)
, do scnf <- cnfSSLConfig cnf
addr <- cnfServerV6Addr cnf
return ( do so <- listenOn AF_INET6 addr (sslServerPort scnf)
launchListener (sslContext scnf, so)
)
, do addr <- cnfServerV4Addr cnf
return ( launchListener =<< listenOn AF_INET addr (cnfServerPort cnf)
)
, do addr <- cnfServerV6Addr cnf
return ( launchListener =<< listenOn AF_INET6 addr (cnfServerPort cnf)
)
]
sequence_ launchers
waitForever
where
launchListener :: SocketLike s => s -> IO ()
launchListener so
= do p <- SL.socketPort so
_ <- forkIO $ httpLoop p so
return ()
listenOn :: Family -> HostName -> ServiceName -> IO Socket
listenOn fam host srv
= do proto <- getProtocolNumber "tcp"
let hints = defaultHints {
addrFlags = [AI_PASSIVE]
, addrFamily = fam
, addrSocketType = Stream
, addrProtocol = proto
}
addrs <- getAddrInfo (Just hints) (Just host) (Just srv)
let addr = head addrs
bracketOnError
(socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
(sClose)
(\ sock ->
do setSocketOption sock ReuseAddr 1
bindSocket sock (addrAddress addr)
listen sock maxListenQueue
return sock
)
httpLoop :: SocketLike s => PortNumber -> s -> IO ()
httpLoop port so
= do (h, addr) <- SL.accept so
tQueue <- newInteractionQueue
readerTID <- forkIO $ requestReader cnf tree fbs h port addr tQueue
_writerTID <- forkIO $ responseWriter cnf h tQueue readerTID
httpLoop port so
waitForever :: IO ()
waitForever = forever (threadDelay 1000000)