module Ketchup.Httpd
( HTTPRequest
, method, uri, httpver, headers
, listenHTTP
) where
import Control.Concurrent (forkIO)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C
import qualified Data.Map as Map
import Network
import qualified Network.Socket as NS
import Network.Socket.ByteString
import Ketchup.Utils
import System.IO
data HTTPRequest = HTTPRequest { method :: B.ByteString
, uri :: B.ByteString
, httpver :: B.ByteString
, headers :: Map.Map B.ByteString [B.ByteString]
} deriving (Show)
parseRequestLine :: B.ByteString -> (B.ByteString, [B.ByteString])
parseRequestLine line =
(property, values)
where
property = head items
values = C.split ',' $ (trim . last) items
items = C.split ':' line
getRequest :: Socket -> IO [B.ByteString]
getRequest client = do
content <- recv client 1024
return $ C.lines content
parseRequest :: [B.ByteString] -> HTTPRequest
parseRequest reqlines =
HTTPRequest { method=met, uri=ur, httpver=ver, headers=heads }
where
[met, ur, ver] = (C.words . head) reqlines
heads = Map.fromList $ map parseRequestLine $ tail reqlines
handleRequest :: Socket -> (Socket -> HTTPRequest -> IO ()) -> IO ()
handleRequest client cback = do
reqlines <- getRequest client
case length reqlines of
0 -> sendBadRequest client
_ -> cback client $ parseRequest reqlines
sClose client
acceptAll :: Socket -> (Socket -> HTTPRequest -> IO ()) -> IO ()
acceptAll sock cback = do
(client, _) <- NS.accept sock
handleRequest client cback
acceptAll sock cback
createAcceptorPool :: Socket -> Int -> (Socket -> HTTPRequest -> IO ()) -> IO ()
createAcceptorPool sock max cback =
case max of
0 -> acceptAll sock cback
x -> do
forkIO $ acceptAll sock cback
createAcceptorPool sock (x1) cback
getHostaddr :: String -> IO NS.HostAddress
getHostaddr "*" = return NS.iNADDR_ANY
getHostaddr host = NS.inet_addr host
listenHTTP :: String -> PortNumber -> (Socket -> HTTPRequest -> IO ()) -> IO ()
listenHTTP hostname port cback = withSocketsDo $ do
host <- getHostaddr hostname
let addr = NS.SockAddrInet port host
sock <- NS.socket NS.AF_INET NS.Stream 0
NS.setSocketOption sock NS.ReuseAddr 1
NS.setSocketOption sock NS.NoDelay 1
NS.bindSocket sock addr
NS.listen sock 128
createAcceptorPool sock 128 cback
sClose sock