module Channel (
Channel (..) ,
ChannelType(..),
stdChan ,
newChan ,
dataBreak )
where
import qualified Network as N
import Network.BSD (getHostName)
import System.IO (Handle, hFlush, hGetLine, hPutStrLn, hShow)
import System.IO.Error (catchIOError)
import Control.Concurrent (forkIO,threadDelay)
import Control.Concurrent.MVar
data Channel = Channel {
chanType :: ChannelType
, clientPort :: Integer
, send :: String -> IO ()
, receive :: IO String
, extra :: [String]
}
data ChannelType = Internal
| Std
| HTTP
| String
deriving (Eq, Show, Read)
stdChan :: Handle -> Channel
stdChan h = Channel Std (-1) write rd []
where
write = hPutStrLn h
rd = hGetLine h
newChan :: ChannelType -> String -> Integer -> IO Channel
newChan t host cp = do
currentHost <- getHostName
case t of
Internal -> newInternalChan currentHost hostPort cp
HTTP
| hostName == "localhost" || hostName == currentHost -> newLocalChan t cp
| otherwise -> newForeignChan t hostName hostPort
String -> newForeignChan t hostName hostPort
where
(hostName, _:hostPort) = break (==':') host
newInternalChan :: String -> String -> Integer -> IO Channel
newInternalChan hostName hostPort cp = return $ Channel Internal cp s r ex
where
r = N.withSocketsDo $ do
inSock <- N.listenOn $ N.PortNumber $ fromIntegral cp
(inHandle,_,_) <- N.accept inSock
msg <- hGetLine inHandle
N.sClose inSock
return msg
s v = N.withSocketsDo $ do
_ <- forkIO $ do
outHandle <- waitForConnect hostName $ N.PortNumber $ port hostPort
hPutStrLn outHandle v
return ()
ex = zipWith (\a b -> (a ++ dataBreak : b)) ["host","clientPort","type"] [hostName ++ ":" ++ hostPort,show cp,show Internal]
newLocalChan :: ChannelType -> Integer -> IO Channel
newLocalChan t cp = N.withSocketsDo $ do
hanVar <- newEmptyMVar
_ <- forkIO $ do
inSock <- N.listenOn $ N.PortNumber $ fromIntegral cp
(inHandle,_,_) <- N.accept inSock
putMVar hanVar inHandle
currentHost <- getHostName
let ex' = ex ++ ["host" ++ dataBreak :currentHost ++ ":" ++ show cp]
return $ Channel t cp (send' hanVar) (receive' hanVar) ex'
where
ex = zipWith (\a b -> (a ++ dataBreak : b)) ["clientPort","type"] [show cp,show t]
newForeignChan :: ChannelType -> String -> String -> IO Channel
newForeignChan t hostName hostPort = N.withSocketsDo $ do
hanVar <- newEmptyMVar
_ <- forkIO $ do
outHandle <- waitForConnect hostName $ N.PortNumber $ port hostPort
putMVar hanVar outHandle
return $ Channel t 0 (send' hanVar) (receive' hanVar) ex
where
ex = zipWith (\a b -> a ++ dataBreak : b) ["host","clientPort","type"] [hostName ++ ":" ++ hostPort,"-1",show t]
port :: String -> N.PortNumber
port s = fromIntegral (read s :: Integer)
dataBreak :: Char
dataBreak = '#'
waitForConnect :: N.HostName -> N.PortID -> IO Handle
waitForConnect h p = N.connectTo h p `catchIOError`
(\_ -> do
threadDelay 10000
putStrLn "waiting for connection"
waitForConnect h p)
send' :: MVar Handle -> String -> IO ()
send' hanVar v = do
han <- takeMVar hanVar
hPutStrLn han v
hFlush han
putMVar hanVar han
receive' :: MVar Handle -> IO String
receive' hanVar = do
han <- readMVar hanVar
msg <- hGetLine han
hFlush han
return msg
printH :: Handle -> IO ()
printH h = do
hstr <- hShow h
putStrLn hstr