module HttpImpl ( startServer ) where import Control.Concurrent import Control.OldException as Exception import Data.Maybe import Hascat.App import Hascat.Config import qualified Hascat.Protocol as HP import Hascat.System.App import Hascat.System.Controller import Hascat.Toolkit hiding (rqMethod, rqURI, rqInputs) import Network.BSD import Network.HTTP as HTTP import Network.Socket --hiding (listen) import Network.URI import Network.TCP import Text.Html import Prelude hiding ( log ) import qualified Data.ByteString.Lazy as Lazy hiding ( pack, unpack,span ) import qualified Data.ByteString.Lazy.Char8 as Lazy ( pack, unpack, span ) import Logger startServer :: StateVar -> IO () startServer stateVar = Exception.catch (runServer stateVar) (\e -> case e of IOException io -> do log (show io) --startServer stateVar _ -> do log (show e) startServer stateVar) runServer :: StateVar -> IO () runServer stateVar = withSocketsDo $ do (State _ general _) <- readMVar stateVar let portNumber = fromIntegral (getPort general) proto <- getProtocolNumber "tcp" Exception.bracket (socket AF_INET Stream proto) sClose (\server -> do setSocketOption server ReuseAddr 1 --setSocketOption server NoDelay 1 bindSocket server (SockAddrInet portNumber iNADDR_ANY) listen server maxListenQueue log ("Waiting for connections on port " ++ show portNumber) acceptConnections stateVar server) acceptConnections :: StateVar -> Socket -> IO () acceptConnections stateVar server = do (client, addr@(SockAddrInet port haddr)) <- accept server --wartet bis Client verbindet --(handle, hostname, port) <- accept server --handle <- socketToHandle client ReadWriteMode {-- hostName <- Exception.catchJust ioErrors (do (HostEntry hostName _ _ _) <- getHostByAddr AF_INET haddr return hostName) (\e -> inet_ntoa haddr)--} -- log ("Accepted connection from " ++ hostname ++ " on port " ++ show port) -- conn <- newIORef (MkConn client addr [] "") -- let stream = ConnRef conn forkIO (do state <- readMVar stateVar handleStream <- socketConnection "" client handleConnection state handleStream HTTP.close handleStream) acceptConnections stateVar server handleConnection :: State -> HandleStream Lazy.ByteString -> IO () handleConnection state stream = do requestResult <- receiveHTTP stream response <- case requestResult of Left error -> return getResponse400 Right request -> respondRequest state request respondHTTP stream response HTTP.close stream respondRequest :: State -> Request Lazy.ByteString -> IO (Response Lazy.ByteString) respondRequest state request = do let method = rqMethod request log (show (rqMethod request) ++ " " ++ show (rqURI request)) if method `elem` [HEAD, GET, POST] then do response <- Exception.catch (respondRequest' state request) (\e -> return $ getResponse500 $ ": " ++ show e) case rspCode response of (2, _, _) | method == HEAD -> return response { rspBody = Lazy.pack "" } _ -> return response else return (getResponse405 method) respondRequest' :: State -> Request Lazy.ByteString -> IO (Response Lazy.ByteString) respondRequest' (State _ _ apps) request = do let path = normalizeCase $ normalizeEscape $ normalizePathSegments $ uriPath (rqURI request) appMB = findApp apps (predicate path) case appMB of Nothing -> return (getResponse404 path) Just app -> let (ContextPath contextPath) = getAppContextPath (appConfig app) in if isRunning app then do let appPath = fromJust $ getRelativePath path contextPath request' = HP.decodeInput $ request { rqURI = (rqURI request) { uriPath = appPath } } -- putStrLn $ "uriPath: " ++ show (uriPath (rqURI request)) -- putStrLn $ "path: " ++ path -- putStrLn $ "appPath: " ++ appPath use app request' else return (getResponse503 contextPath) where predicate :: String -> App -> Bool predicate path app = let (ContextPath contextPath) = getAppContextPath (appConfig app) in getRelativePath path contextPath /= Nothing