{-# LANGUAGE Trustworthy #-} {-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} module Hails.HttpServer.Types ( -- * Requests Request(..) , getRequestBodyType, RequestBodyType(..) , addRequestHeader, removeRequestHeader -- * Responses , Response(..) , module Network.HTTP.Types , addResponseHeader, removeResponseHeader -- * Applications and middleware , Application, RequestConfig(..) , Middleware ) where import qualified Data.List as List import Data.Text (Text) import Data.Typeable import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy as L import Network.Socket (SockAddr) import Network.HTTP.Types import Network.Wai.Parse (RequestBodyType(..)) import Data.Time (UTCTime) import LIO.DCLabel -- -- Request -- -- | A request sent by the end-user. data Request = Request { -- | HTTP Request (e.g., @GET@, @POST@, etc.). requestMethod :: Method -- | HTTP version (e.g., 1.1 or 1.0). , httpVersion :: HttpVersion -- | Extra path information sent by the client. , rawPathInfo :: S.ByteString -- | If no query string was specified, this should be empty. This value -- /will/ include the leading question mark. -- Do not modify this raw value- modify queryString instead. , rawQueryString :: S.ByteString -- | Generally the host requested by the user via the Host request header. -- Backends are free to provide alternative values as necessary. This value -- should not be used to construct URLs. , serverName :: S.ByteString -- | The request headers. , requestHeaders :: RequestHeaders -- | Was this request made over an SSL connection? , isSecure :: Bool -- | The client\'s host information. , remoteHost :: SockAddr -- | Path info in individual pieces- the url without a hostname/port -- and without a query string, split on forward slashes, , pathInfo :: [Text] -- | Parsed query string information , queryString :: Query -- | Lazy ByteString containing the request body. , requestBody :: L.ByteString -- | Time request was received. , requestTime :: UTCTime } deriving (Show, Typeable) -- | Get the request body type (copied from @wai-extra@). getRequestBodyType :: Request -> Maybe RequestBodyType getRequestBodyType req = do ctype <- lookup "Content-Type" $ requestHeaders req if urlenc `S.isPrefixOf` ctype then Just UrlEncoded else case boundary ctype of Just x -> Just $ Multipart x Nothing -> Nothing where urlenc = S8.pack "application/x-www-form-urlencoded" formBound = S8.pack "multipart/form-data;" bound' = "boundary=" boundary s = if "multipart/form-data;" `S.isPrefixOf` s then let s' = S.dropWhile (== 32) $ S.drop (S.length formBound) s in if bound' `S.isPrefixOf` s' then Just $ S.drop (S.length bound') s' else Nothing else Nothing -- | Add/replace a 'Header' to the 'Request' addRequestHeader :: Request -> Header -> Request addRequestHeader req hdr@(hname, _) = req { requestHeaders = hdr:headers } where headers = List.filter ((/= hname) . fst) $ requestHeaders req -- | Remove a header (if it exists) from the 'Request' removeRequestHeader :: Request -> HeaderName -> Request removeRequestHeader req hname = req { requestHeaders = headers } where headers = List.filter ((/= hname) . fst) $ requestHeaders req -- -- Response -- -- | A response sent by the app. data Response = Response { -- | Response status respStatus :: Status -- | Response headers , respHeaders :: ResponseHeaders -- | Response body , respBody :: L.ByteString } deriving (Show, Typeable) -- | Add/replace a 'Header' to the 'Response' addResponseHeader :: Response -> Header -> Response addResponseHeader resp hdr@(hname, _) = resp { respHeaders = hdr:headers } where headers = List.filter ((/= hname) . fst) $ respHeaders resp -- | Remove a header (if it exists) from the 'Response' removeResponseHeader :: Response -> HeaderName -> Response removeResponseHeader resp hname = resp { respHeaders = headers } where headers = List.filter ((/= hname) . fst) $ respHeaders resp -- -- Application & middleware -- -- | The settings with which the app will run. data RequestConfig = RequestConfig { -- | The label of the browser the reponse will be sent to. browserLabel :: DCLabel -- | The label of the incoming request (with the logged in user's integrity). , requestLabel :: DCLabel -- | A privilege minted for the app. , appPrivilege :: DCPriv } deriving (Show, Typeable) -- | Base Hails type implemented by untrusted applications. type Application = RequestConfig -> DCLabeled Request -> DC Response -- | Convenience type for middleware components. type Middleware = Application -> Application