{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE OverloadedStrings #-} module Server where import Network.Socket import Data.ByteString.Lazy.Char8 as L ( fromChunks , putStrLn ) import Data.ByteString.Char8 ( hGetNonBlocking ) import System.IO ( IOMode(..) , hSetBuffering , BufferMode(..) , hWaitForInput , hClose , hIsEOF ) import Control.Monad import Control.Concurrent (forkIO) import Control.Exception (handle,SomeException(..)) import Data.HList import Data.HList.TypeEqGeneric1() import Data.HList.TypeCastGeneric1() newtype ConnId = ConnId Int deriving Eq newtype ConnectionFinalizer = ConnectionFinalizer (IO ()) getPacket h = do { hWaitForInput h (-1) ; fmap (fromChunks . (:[])) $ hGetNonBlocking h 1024 } {- doServer :: HList st => PortNumber :*: st -> ( Handle :*: ConnId :*: PortNumber :*: st -> S.ByteString -> (() -> IO ()) -> IO () ) -> IO b -} doServer port g startCon = runServer2 port (runConn2 g) where runConn2 g st (sock,_) = do h <- socketToHandle sock ReadWriteMode hSetBuffering h NoBuffering st'' <- startCon sock (h .*. st) let doException (SomeException e) = Prelude.putStrLn ("\n\nexception: " ++ show e ++ "\n\n") handle doException $ do -- PEER CRED: (0,4294967295,4294967295) -- PEER NAME: 127.0.0.1:37253 handle doException $ fix $ \loop -> do let continue () = hIsEOF h >>= flip when loop . not packet <- getPacket h g st'' packet continue let ConnectionFinalizer cleanup = hOccursFst st'' cleanup hClose h {- runServer2 :: Num num => PortNumber -> (num -> (Socket, SockAddr) -> IO b -> IO b) -> IO b -} runServer2 st@(HCons port _) go = do sock <- socket AF_INET Stream 0 setSocketOption sock ReuseAddr 1 bindSocket sock (SockAddrInet port iNADDR_ANY) listen sock 2 forkIO $ do mainLoop sock (ConnId 0) go L.putStrLn $ "quit accept loop" return sock where mainLoop sock idnum@(ConnId n) go = do let doException (SomeException e) = do Prelude.putStrLn ("\n\naccept-loop exception: " ++ show e ++ "\n\n") return Nothing mcon <- handle doException $ fix $ \loop -> do con <- accept sock return $ Just con case mcon of Just con -> do forkIO $ go (idnum .*. st) con mainLoop sock (ConnId (n+1)) go Nothing -> return ()