hsp-0.2: HSP/Data/RequestEnv.hs
{-# LANGUAGE PatternGuards #-}
-----------------------------------------------------------------------------
-- |
-- Module : HSP.Data.RequestEnv
-- Copyright : (c) Niklas Broberg 2004,
-- License : BSD-style (see the file LICENSE.txt)
--
-- Maintainer : Niklas Broberg, d00nibro@dtek.chalmers.se
-- Stability : experimental
-- Portability : requires undecidable and overlapping instances
--
-- Defines the RequestEnv object available to HSP pages,
-- either in the form of a complete Request structure,
-- or as a set of CGI environment variables.
-----------------------------------------------------------------------------
module HSP.Data.RequestEnv (
RequestEnv -- ^ The Request type is abstract
, newRequest -- ^ :: HTTP.Request.Request -> RequestEnv
, newCGIReq -- ^ :: CGI.CGIEnv -> RequestEnv
, getVarValue -- ^ :: RequestEnv -> String -> Maybe String
, getMethod -- ^ :: RequestEnv -> Method
, getCookie -- ^ :: RequestEnv -> String -> Maybe String
, Method(..) -- ^ An HTTP Method
, RequestURI(..)
, HTTPVersion(..)
) where
import HTTP.Request
import CGI.CGIEnv
import Network.URI
import qualified Data.Map as M
{- |
The Request datatype is a repository of request scope variables,
together with extra information on the incomming HTTP request such
as headers, method etc.
-}
data RequestEnv = RequestEnv {
dataRep :: M.Map String [String],
cookies :: M.Map String String,
rawData :: Either Request CGIEnv
}
-- | Build a HSP Request object given a parsed HTTP request.
newRequest :: Request -> RequestEnv
newRequest req =
let headers = reqHeaders req
mch = lookupHeader headers "Cookie"
ckies = case mch of
Nothing -> []
Just str -> csToVars str
in case reqMethod req of
GET -> let vars = readURIVars (reqURI req)
in RequestEnv {
dataRep = M.fromListWith (++) vars,
cookies = M.fromList ckies,
rawData = Left req }
_ -> RequestEnv {
dataRep = M.empty,
cookies = M.fromList ckies,
rawData = Left req }
where readURIVars :: RequestURI -> [(String, [String])]
readURIVars uri = case uri of
AbsURI uri -> qsToVars (uriQuery uri)
AbsPath p -> case dropWhile (/='?') p of
"" -> []
(_:qs) -> qsToVars qs
_ -> []
lookupHeader :: [Header] -> String -> Maybe String
lookupHeader [] _ = Nothing
lookupHeader ((Header (fn, fv)):hs) str =
if fn == str then Just fv else lookupHeader hs str
newCGIReq :: CGIEnv -> RequestEnv
newCGIReq cgienv@(rvars,rbody) =
let vars = case lookup "REQUEST_METHOD" rvars of
Just "POST" -> newCGIPostReq cgienv
_ -> newCGIGetReq rvars
ckies = case lookup "HTTP_COOKIE" rvars of
Just cs -> M.fromList $ csToVars cs
Nothing -> M.empty
in RequestEnv {
dataRep = vars,
cookies = ckies,
rawData = Right cgienv }
newCGIGetReq :: [CGIVariable] -> M.Map String [String]
newCGIGetReq cgienv =
case lookup "QUERY_STRING" cgienv of
Just qs -> M.fromListWith (++) $ qsToVars qs
Nothing -> M.empty
newCGIPostReq :: CGIEnv -> M.Map String [String]
newCGIPostReq (cgienv,rbody) =
case lookup "CONTENT_LENGTH" cgienv of
-- if content length is not a valid int an exception is thrown
Just l -> M.fromListWith (++) $ qsToVars $ take (read l) rbody
Nothing -> M.empty
qsToVars :: String -> [(String, [String])]
qsToVars [/ ks@_+, '=', vs@_*, (/ '&', kss@:_+, '=', vss@:_* /)* /]
= zip (ks:kss) $ map (\s -> [unEscape s]) (vs:vss)
qsToVars "" = [] -- TODO: What if there's an error?
csToVars :: String -> [(String, String)]
csToVars [/ ' '*!, ks@_+, ' '*!, '=', ' '*!, vs@_+, ' '*!,
(/ ';', ' '*!, kss@:_+, ' '*!, '=', ' '*!, vss@:_+, ' '*! /)* /]
= zip (ks:kss) (vs:vss)
csToVars "" = []
unEscape s = unEscapeString $ map (\ch -> if ch == '+' then ' ' else ch) s
-- | Return the value of a request scope variable, if available.
getVarValue :: RequestEnv -> String -> Maybe [String]
getVarValue req key = M.lookup key (dataRep req)
-- | Return the HTTP method for this request.
getMethod :: RequestEnv -> Method
getMethod = either reqMethod (maybe GET read . lookup "REQUEST_METHOD" . fst) . rawData -- TODO: what to do on error?
-- | Return the value of a Cookie
getCookie :: RequestEnv -> String -> Maybe String
getCookie req co = M.lookup co (cookies req)
{-
-- | Return the URI for this request.
getURI :: RequestEnv -> RequestURI
getURI re = case rawData re of
Left req -> reqURI req
Right -> ???
-- | Return the HTTP version used in this request.
getVersion :: RequestEnv -> HTTPVersion
getVersion re = either reqVersion
(maybe http1_1 read . lookup "SERVER_PROTOCOL") -- ?? shouldn't that be client?
-- | Return the value associated with a given header, if
-- the header was set in the incomming request.
getHeader :: RequestEnv -> String -> Maybe String
getHeader req hd =
let headers = reqHeaders $ rawRequest req
in lookupHeader hd headers
where lookupHeader hd [] = Nothing
lookupHeader hd (Header (hn, hv):hds)
| hd == hn = Just hv
| otherwise = lookupHeader hd hds
-}