module Network.HTTP.Lucu.Preprocess
( preprocess
)
where
import Control.Concurrent.STM
import Control.Monad
import qualified Data.ByteString as Strict (ByteString)
import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
import Data.Char
import Data.Maybe
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.Headers
import Network.HTTP.Lucu.HttpVersion
import Network.HTTP.Lucu.Interaction
import Network.HTTP.Lucu.Request
import Network.HTTP.Lucu.Response
import Network
import Network.URI
preprocess :: Interaction -> STM ()
preprocess itr
= itr `seq`
do req <- readItr itr itrRequest fromJust
let reqVer = reqVersion req
if reqVer /= HttpVersion 1 0 &&
reqVer /= HttpVersion 1 1 then
do setStatus HttpVersionNotSupported
writeItr itr itrWillClose True
else
do when (reqVer == HttpVersion 1 0)
$ writeItr itr itrWillClose True
completeAuthority req
case reqMethod req of
GET -> return ()
HEAD -> writeItr itr itrWillDiscardBody True
POST -> writeItr itr itrRequestHasBody True
PUT -> writeItr itr itrRequestHasBody True
DELETE -> return ()
_ -> setStatus NotImplemented
preprocessHeader req
where
setStatus :: StatusCode -> STM ()
setStatus status
= status `seq`
updateItr itr itrResponse
$! \ res -> res {
resStatus = status
}
completeAuthority :: Request -> STM ()
completeAuthority req
= req `seq`
when (uriAuthority (reqURI req) == Nothing)
$ if reqVersion req == HttpVersion 1 0 then
do let conf = itrConfig itr
host = cnfServerHost conf
port = case cnfServerPort conf of
PortNumber n -> Just (fromIntegral n :: Int)
_ -> Nothing
portStr
= case port of
Just 80 -> Just ""
Just n -> Just $ ":" ++ show n
Nothing -> Nothing
case portStr of
Just str -> updateAuthority host (C8.pack str)
Nothing -> setStatus InternalServerError
else
do case getHeader (C8.pack "Host") req of
Just str -> let (host, portStr) = parseHost str
in updateAuthority host portStr
Nothing -> setStatus BadRequest
parseHost :: Strict.ByteString -> (Strict.ByteString, Strict.ByteString)
parseHost = C8.break (== ':')
updateAuthority :: Strict.ByteString -> Strict.ByteString -> STM ()
updateAuthority host portStr
= host `seq` portStr `seq`
updateItr itr itrRequest
$! \ (Just req) -> Just req {
reqURI = let uri = reqURI req
in uri {
uriAuthority = Just URIAuth {
uriUserInfo = ""
, uriRegName = C8.unpack host
, uriPort = C8.unpack portStr
}
}
}
preprocessHeader :: Request -> STM ()
preprocessHeader req
= req `seq`
do case getHeader (C8.pack "Expect") req of
Nothing -> return ()
Just value -> if value `noCaseEq` C8.pack "100-continue" then
writeItr itr itrExpectedContinue True
else
setStatus ExpectationFailed
case getHeader (C8.pack "Transfer-Encoding") req of
Nothing -> return ()
Just value -> if value `noCaseEq` C8.pack "identity" then
return ()
else
if value `noCaseEq` C8.pack "chunked" then
writeItr itr itrRequestIsChunked True
else
setStatus NotImplemented
case getHeader (C8.pack "Content-Length") req of
Nothing -> return ()
Just value -> if C8.all isDigit value then
do let Just (len, _) = C8.readInt value
writeItr itr itrReqChunkLength $ Just len
writeItr itr itrReqChunkRemaining $ Just len
else
setStatus BadRequest
case getHeader (C8.pack "Connection") req of
Nothing -> return ()
Just value -> if value `noCaseEq` C8.pack "close" then
writeItr itr itrWillClose True
else
return ()