packages feed

darcsden-1.1: src/WebServer.hs

{-# LANGUAGE OverloadedStrings, CPP #-}
module Main where

import Blaze.ByteString.Builder (fromByteString, toByteString)
import Blaze.ByteString.Builder.Char8 (fromShow)
-- #ifdef DEBUG
-- import Blaze.ByteString.Builder.Char8 (fromString)
-- #endif
#ifdef WIN32
import Control.Concurrent (forkIO, killThread)
#endif
import Control.Exception
import Control.Monad.Trans
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8 (pack)
import Data.Monoid (mconcat)
import Snap.Core
import Snap.Http.Server
import System.Directory (getCurrentDirectory)
import System.Environment
import OpenSSL (withOpenSSL)
import System.IO ( Handle, stdout, hPutStrLn )
#ifdef WIN32
import System.Win32.SystemServices.Wrapper ( defineService, Service(..) )
#endif

-- import DarcsDen.Debug
import DarcsDen.Handler
import DarcsDen.Settings
import DarcsDen.State.Util (runDB, withRedis, createFreshState)
import DarcsDen.WebUtils (exceptionPage)


main :: IO ()
main = withOpenSSL $ do
    cwd <- liftIO getCurrentDirectory
    as <- getArgs
    go False stdout cwd as

go :: Bool -> Handle -> FilePath -> [String] -> IO ()
go noVerboseSnap outH cwd as =
    case as of
        ("--install":_) -> runDB $ createFreshState True

        ("--root":root:as') -> go noVerboseSnap outH root as'

#ifdef WIN32
        ("--service":as') ->
            defineService $
                Service {
                    serviceName = "Darcsden",
                    -- when wrapping as a service, we have to take care that the
                    -- service code doesn't try to use stdout/stderr, as they
                    -- don't exist. This means stopping Snap from being verbose,
                    -- and redirecting our own output to the service wrapper's log.
                    serviceStart = \debugH -> forkIO (go True debugH cwd as'),
                    serviceStop = killThread
                }
#endif

        [] -> do
            checkDBs
            hPutStrLn outH "darcsden running on port 8900"
            startHTTP 8900 hostname cwd

        ("--port":p:_) -> do
            checkDBs
            hPutStrLn outH $ "darcsden running on port " ++ p
            startHTTP (read p) hostname cwd

        _ ->
            putStr . unlines $
                [ "usage:"
                , "  darcsden --readme    : figure out how to use this thing"
                , "  darcsden --install   : set up CouchDB databases"
                , "  darcsden             : start webserver with defaults"
                , "  darcsden --port PORT : start webserver on given port"
                , "  darcsden --root DIR  : select root directory (defaults to current directory)"
#ifdef WIN32
                , "  darcsden --service   : run as a Windows service"
#endif
                ]
  where
    checkDBs = do
        hPutStrLn outH "checking couchdb..."
        runDB (return ())

        hPutStrLn outH "checking redis..."
        withRedis (return ())

    startHTTP p h d = httpServe (config p h) (handler d)

    config p h
        = setAccessLog (ConfigFileLog $ accessLog)
        . setErrorLog (ConfigFileLog $ errorLog)
        . setPort p
        . setHostname (B8.pack h)
        . setDefaultTimeout maxRequestTime
        . setErrorHandler errorHandler
        . (if noVerboseSnap then setVerbose False else id)
        $ defaultConfig

errorHandler :: SomeException -> Snap ()
errorHandler e = do
    r <- getRequest
    logError $ errorLogMsg e r
    exceptionPage e

errorLogMsg :: SomeException -> Request -> B.ByteString
errorLogMsg e r =
  toByteString $ mconcat $
  [ fromByteString "During processing of request from "
  , fromByteString $ rqRemoteAddr r
  , fromByteString ":"
  , fromShow $ rqRemotePort r
  , fromByteString "\n"
  , fromByteString "Request handler threw an exception:\n"
  , fromShow e
-- #ifdef DEBUG
--   , fromByteString "Request:\n"
--   , fromString $ show r
--   , fromByteString "\n"
-- #endif
  ]