packages feed

happstack-server 0.2.1 → 0.3.1

raw patch · 11 files changed

+414/−254 lines, 11 filesdep +utf8-stringdep ~happstack-datadep ~happstack-utilPVP ok

version bump matches the API change (PVP)

Dependencies added: utf8-string

Dependency ranges changed: happstack-data, happstack-util

API changes (from Hackage documentation)

- Happstack.Server.JSON: JBool :: Bool -> JSON
- Happstack.Server.JSON: JCall :: CallBack -> x -> JSONCall x
- Happstack.Server.JSON: JFloat :: Float -> JSON
- Happstack.Server.JSON: JInt :: Int -> JSON
- Happstack.Server.JSON: JList :: [JSON] -> JSON
- Happstack.Server.JSON: JNull :: JSON
- Happstack.Server.JSON: JObj :: [(String, JSON)] -> JSON
- Happstack.Server.JSON: JString :: String -> JSON
- Happstack.Server.JSON: class ToJSON a
- Happstack.Server.JSON: data JSON
- Happstack.Server.JSON: data JSONCall x
- Happstack.Server.JSON: instance [overlap ok] ToJSON JSON
- Happstack.Server.JSON: jInt :: (Integral a) => a -> JSON
- Happstack.Server.JSON: jsonToString :: JSON -> String
- Happstack.Server.JSON: toJSON :: (ToJSON a) => a -> JSON
- Happstack.Server.JSON: type CallBack = String
+ Happstack.Server.HTTP.FileServe: doIndexStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m) => [String] -> MimeMap -> String -> m Response
+ Happstack.Server.HTTP.FileServe: fileServeStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m) => [FilePath] -> FilePath -> m Response
+ Happstack.Server.SimpleHTTP: type UnWebT m a = m (Maybe (Either Response a, FilterFun Response))
- Happstack.Server.HTTP.FileServe: fileServe :: (ServerMonad m, FilterMonad Response m, MonadIO m) => [FilePath] -> FilePath -> m Response
+ Happstack.Server.HTTP.FileServe: fileServe :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m) => [FilePath] -> FilePath -> m Response
- Happstack.Server.SimpleHTTP: applyRequest :: (ToMessage a, Monad m) => ServerPartT m a -> Request -> Either (m Response) b
+ Happstack.Server.SimpleHTTP: applyRequest :: (ToMessage a, Monad m, Functor m) => ServerPartT m a -> Request -> Either (m Response) b
- Happstack.Server.SimpleHTTP: mapServerPartT :: (m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> ServerPartT m a -> ServerPartT n b
+ Happstack.Server.SimpleHTTP: mapServerPartT :: (UnWebT m a -> UnWebT n b) -> (ServerPartT m a -> ServerPartT n b)
- Happstack.Server.SimpleHTTP: mapServerPartT' :: (Request -> m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> ServerPartT m a -> ServerPartT n b
+ Happstack.Server.SimpleHTTP: mapServerPartT' :: (Request -> UnWebT m a -> UnWebT n b) -> (ServerPartT m a -> ServerPartT n b)
- Happstack.Server.SimpleHTTP: mapWebT :: (m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> WebT m a -> WebT n b
+ Happstack.Server.SimpleHTTP: mapWebT :: (UnWebT m a -> UnWebT n b) -> (WebT m a -> WebT n b)
- Happstack.Server.SimpleHTTP: mkWebT :: m (Maybe (Either Response a, FilterFun Response)) -> WebT m a
+ Happstack.Server.SimpleHTTP: mkWebT :: UnWebT m a -> WebT m a
- Happstack.Server.SimpleHTTP: runWebT :: (ToMessage b, Monad m) => WebT m b -> m (Maybe Response)
+ Happstack.Server.SimpleHTTP: runWebT :: (Functor m, ToMessage b) => WebT m b -> m (Maybe Response)
- Happstack.Server.SimpleHTTP: simpleHTTP' :: (Monad m, ToMessage b) => (m (Maybe (Either Response a, FilterFun Response)) -> IO (Maybe (Either Response b, FilterFun Response))) -> Conf -> ServerPartT m a -> IO ()
+ Happstack.Server.SimpleHTTP: simpleHTTP' :: (ToMessage b, Monad m, Functor m) => (UnWebT m a -> UnWebT IO b) -> Conf -> ServerPartT m a -> IO ()
- Happstack.Server.SimpleHTTP: spUnwrapErrorT :: (Monad m) => (e -> ServerPartT m a) -> Request -> ErrorT e m (Maybe (Either Response a, FilterFun Response)) -> m (Maybe (Either Response a, FilterFun Response))
+ Happstack.Server.SimpleHTTP: spUnwrapErrorT :: (Monad m) => (e -> ServerPartT m a) -> Request -> UnWebT (ErrorT e m) a -> UnWebT m a
- Happstack.Server.SimpleHTTP: ununWebT :: WebT m a -> m (Maybe (Either Response a, FilterFun Response))
+ Happstack.Server.SimpleHTTP: ununWebT :: WebT m a -> UnWebT m a

Files

happstack-server.cabal view
@@ -1,5 +1,5 @@ Name:                happstack-server-Version:             0.2.1+Version:             0.3.1 Synopsis:            Web related tools and services. Description:         Web framework License:             BSD3@@ -9,8 +9,13 @@ homepage:            http://happstack.com Category:            Web, Distributed Computing Build-Type:          Simple-Cabal-Version:       >= 1.4+Cabal-Version:       >= 1.6 +source-repository head+    type:     darcs+    subdir:   happstack-server+    location: http://patch-tag.com/publicrepos/happstack+ Flag base4     Description: Choose the even newer, even smaller, split-up base package. @@ -28,7 +33,6 @@                        Happstack.Server.HTTP.LowLevel                        Happstack.Server.HTTP.FileServe                        Happstack.Server.SimpleHTTP-                       Happstack.Server.JSON                        Happstack.Server.MessageWrap                        Happstack.Server.MinHaXML                        Happstack.Server.SURI@@ -61,8 +65,8 @@                        extensible-exceptions,                        HaXml >= 1.13 && < 1.14,                        hslogger >= 1.0.2,-                       happstack-data >= 0.2.1 && < 0.3,-                       happstack-util >= 0.2.1 && < 0.3,+                       happstack-data >= 0.3.1 && < 0.4,+                       happstack-util >= 0.3.1 && < 0.4,                        html,                        MaybeT,                        mtl,@@ -73,6 +77,7 @@                        process,                        template-haskell,                        time,+                       utf8-string >= 0.3.4 && < 0.4,                        xhtml,                        zlib 
src/Happstack/Server/HTTP/Client.hs view
@@ -10,6 +10,8 @@ import qualified Data.ByteString.Char8 as B  import Network +-- | Sends the serialized request to the host defined in the request+-- and attempts to parse response upon arrival. getResponse :: Request -> IO (Either String Response) getResponse rq = withSocketsDo $ do   let (hostName,p) = span (/=':') $ fromJust $ fmap B.unpack $ getHeader "host" rq 
src/Happstack/Server/HTTP/Clock.hs view
@@ -22,7 +22,7 @@   return ref  updater :: IORef B.ByteString -> IO ()-updater ref = do threadDelay (10^(6 :: Int) * 1) -- Every second+updater ref = do threadDelay (10^(6 :: Int)) -- Every second                  writeIORef ref =<< mkTime                  updater ref 
src/Happstack/Server/HTTP/FileServe.hs view
@@ -1,7 +1,15 @@-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleContexts, Rank2Types #-} module Happstack.Server.HTTP.FileServe     (-     MimeMap,fileServe, mimeTypes,isDot, blockDotFiles,doIndex,errorwrapper+     MimeMap,+     blockDotFiles,+     doIndex,+     doIndexStrict,+     errorwrapper,+     fileServe,+     fileServeStrict,+     isDot,+     mimeTypes     ) where  import Control.Exception.Extensible@@ -34,37 +42,102 @@                 do bintime <- getModificationTime binarylocation                    logtime <- getModificationTime loglocation                    if (logtime > bintime)-                     then fmap Just $ readFile loglocation -- fileServe [loglocation] [] "./"+                     then fmap Just $ readFile loglocation                      else return Nothing + type MimeMap = Map.Map String String +type GetFileFunc = (MonadIO m) =>+    Map.Map String String+    -> String+    -> m (Either String ((ClockTime, Integer), (String, L.ByteString)))+++ doIndex :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>            [String] -> MimeMap -> String -> m Response-doIndex [] _mime _fp = do forbidden $ toResponse "Directory index forbidden"-doIndex (index:rest) mime fp =+doIndex = doIndex' getFile++-- | A variant of 'doIndex' that relies on 'getFileStrict'+doIndexStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>+                 [String] -> MimeMap -> String -> m Response+doIndexStrict = doIndex' getFileStrict+++doIndex' :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>+            GetFileFunc+         -> [String]+         -> MimeMap+         -> String+         -> m Response+doIndex' _getFileFunc []           _mime _fp = forbidden $ toResponse "Directory index forbidden"+doIndex'  getFileFunc (index:rest)  mime  fp =     do     let path = fp++'/':index     --print path     fe <- liftIO $ doesFileExist path     if fe then retFile path else doIndex rest mime fp-    where retFile = returnFile mime+    where retFile = returnFile getFileFunc mime+++ defaultIxFiles :: [String] defaultIxFiles= ["index.html","index.xml","index.gif"]  -fileServe :: (ServerMonad m, FilterMonad Response m, MonadIO m) => [FilePath] -> FilePath -> m Response-fileServe ixFiles localpath  = -    fileServe' localpath (doIndex (ixFiles++defaultIxFiles)) mimeTypes+-- | Serve a file (lazy version). For efficiency reasons when serving large+-- files, will escape the computation early if a file is successfully served,+-- to prevent filters from being applied; if a filter were applied, we would+-- need to compute the content-length (thereby forcing the spine of the+-- ByteString into memory) rather than reading it from the filesystem.+--+-- Note that using lazy fileServe can result in some filehandles staying open+-- until the garbage collector gets around to closing them.+fileServe :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m) =>+             [FilePath]         -- ^ index files if the path is a directory+          -> FilePath           -- ^ file/directory to serve+          -> m Response+fileServe ixFiles localpath = do+    resp <- fileServe' localpath+                       (doIndex (ixFiles++defaultIxFiles))+                       mimeTypes+                       getFile +    escape' $ resp { rsFlags = RsFlags {rsfContentLength=False} }++++-- | Serve a file (strict version). Reads the entire file strictly into+-- memory, and ensures that the handle is properly closed. Unlike lazy+-- fileServe, this function doesn't shortcut the computation early, and it+-- allows for filtering (ex: gzip compression) to be applied+fileServeStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>+                   [FilePath]   -- ^ index files if the path is a directory+                -> FilePath     -- ^ file/directory to serve+                -> m Response+fileServeStrict ixFiles localpath = do+    resp <- fileServe' localpath+                       (doIndex (ixFiles++defaultIxFiles))+                       mimeTypes+                       getFileStrict++    -- clear "Content-Length" because it could be modified by filters+    -- downstream+    let headers = rsHeaders resp+    return $ resp {rsHeaders = Map.delete (P.pack "content-length") headers}+++ -- | Serve files with a mime type map under a directory. --   Uses the function to transform URIs to FilePaths. fileServe' :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>               String               -> (Map.Map String String -> String -> m Response)               -> Map.Map String String+              -> GetFileFunc               -> m Response-fileServe' localpath fdir mime = do+fileServe' localpath fdir mime getFileFunc = do     rq <- askRq     let fp2 = takeWhile (/=',') fp         fp = filepath@@ -84,20 +157,22 @@                | True = "NOT FOUND"     liftIO $ logM "Happstack.Server.HTTP.FileServe" DEBUG ("fileServe: "++show fp++" \t"++status)     if de then fdir mime fp else do-    getFile mime fp >>= flip either (renderResponse mime) -                (const $ returnGroup localpath mime safepath)+    getFileFunc mime fp >>= flip either (renderResponse mime)+        (const $ returnGroup localpath mime safepath) + returnFile :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>-              Map.Map String String -> String -> m Response-returnFile mime fp =  -    getFile mime fp >>=  either fileNotFound (renderResponse mime)+              GetFileFunc -> Map.Map String String -> String -> m Response+returnFile getFileFunc mime fp =+    getFileFunc mime fp >>=  either fileNotFound (renderResponse mime) + -- if fp has , separated then return concatenation with content-type of last -- and last modified of latest tr :: (Eq a) => a -> a -> [a] -> [a] tr a b = map (\x->if x==a then b else x) ltrim :: String -> String-ltrim = dropWhile (flip elem " \t\r")   +ltrim = dropWhile (flip elem " \t\r")  returnGroup :: (ServerMonad m, FilterMonad Response m, MonadIO m) =>                String -> Map.Map String String -> [String] -> m Response@@ -110,7 +185,7 @@   mbFiles <-  mapM (getFile mime) $ fps   let notFounds = [x | Left x <- mbFiles]       files = [x | Right x <- mbFiles]-  if not $ null notFounds +  if not $ null notFounds     then fileNotFound $ drop (length localPath) $ head notFounds else do   let totSize = sum $ map (snd . fst) files       maxTime = maximum $ map (fst . fst) files :: ClockTime@@ -121,8 +196,9 @@   fileNotFound :: (Monad m, FilterMonad Response m) => String -> m Response-fileNotFound fp = do setResponseCode 404 -                     return $ toResponse $ "File not found "++ fp+fileNotFound fp = return $ result 404 $ "File not found " ++ fp++ --fakeLen = 71* 1024 fakeFile :: (Integral a) =>             a -> ((ClockTime, Int64), (String, L.ByteString))@@ -131,6 +207,9 @@       body = L.pack $ (("//"++(show len)++" ") ++ ) $ (replicate len '0') ++ "\n"       len = fromIntegral fakeLen +-- | @getFile mimeMap path@ will lazily read the file as a ByteString+-- with a content type provided by matching the file extension with the+-- @mimeMap@.  getFile will return an error string or ((timeFetched,size), (contentType,fileContents)) getFile :: (MonadIO m) =>            Map.Map String String            -> String@@ -139,13 +218,31 @@   let ct = Map.findWithDefault "text/plain" (getExt fp) mime   fe <- liftIO $ doesFileExist fp   if not fe then return $ Left fp else do-  -  time <- liftIO  $ getModificationTime fp-  h <- liftIO $ openBinaryFile fp ReadMode++  time <- liftIO $ getModificationTime fp+  h    <- liftIO $ openBinaryFile fp ReadMode   size <- liftIO $ hFileSize h-  lbs <- liftIO $ L.hGetContents h+  lbs  <- liftIO $ L.hGetContents h   return $ Right ((time,size),(ct,lbs)) +-- | As 'getFile' but strictly fetches the file, instead of lazily.+getFileStrict :: (MonadIO m) =>+                 Map.Map String String+              -> String+              -> m (Either String ((ClockTime, Integer), (String, L.ByteString)))+getFileStrict mime fp = do+  let ct = Map.findWithDefault "text/plain" (getExt fp) mime+  fe     <- liftIO $ doesFileExist fp++  if not fe then return $ Left fp else do++  time     <- liftIO $ getModificationTime fp+  s        <- liftIO $ P.readFile fp+  let lbs  = L.fromChunks [s]+  let size = toInteger . P.length $ s+  return $ Right ((time,size),(ct,lbs))++ renderResponse :: (Monad m,                    ServerMonad m,                    FilterMonad Response m,@@ -156,21 +253,21 @@ renderResponse _ ((modtime,size),(ct,body)) = do   rq <- askRq   let notmodified = getHeader "if-modified-since" rq == Just (P.pack $ repr)-      repr = formatCalendarTime defaultTimeLocale +      repr = formatCalendarTime defaultTimeLocale              "%a, %d %b %Y %X GMT" (toUTCTime modtime)   -- "Mon, 07 Jan 2008 19:51:02 GMT"   -- when (isJust $ getHeader "if-modified-since"  rq) $ error $ show $ getHeader "if-modified-since" rq-  if notmodified then do setResponseCode 304 ; return $ toResponse "" else do-  --  modifyResponse (setHeader "HUH" $ show $ (fmap P.unpack mod == Just repr,mod,Just repr))-  setHeaderM "Last-modified" repr-  -- if %Z or UTC are in place of GMT below, wget complains that the last-modified header is invalid-  setHeaderM "Content-Length" (show size)-  setHeaderM "Content-Type" ct-  return $ resultBS 200 body+  if notmodified then+      return $ result 304 ""+    else do+      return $ ((setHeader "Last-modified" repr) .+                (setHeader "Content-Length" (show size)) .+                (setHeader "Content-Type" ct)) $+               resultBS 200 body -                + getExt :: String -> String getExt = reverse . takeWhile (/='.') . reverse @@ -181,6 +278,7 @@ 	    ,("xsl","application/xml") 	    ,("js","text/javascript") 	    ,("html","text/html")+	    ,("htm","text/html") 	    ,("css","text/css") 	    ,("gif","image/gif") 	    ,("jpg","image/jpeg")
src/Happstack/Server/HTTP/Handler.hs view
@@ -35,8 +35,6 @@ import Data.Time.Clock (getCurrentTime) import System.Log.Logger (Priority(..), logM) -log' = logM "Happstack.Server"- request :: Conf -> Handle -> Host -> (Request -> IO Response) -> IO () request conf h host handler = rloop conf h host handler =<< L.hGetContents h @@ -84,16 +82,29 @@                      time <- getCurrentTime                      let host' = fst host                          user = "-"-                         requestLine = unwords [show $ rqMethod req, rqUri req, show $ rqVersion req]+                         requestLn = unwords [show $ rqMethod req, rqUri req, show $ rqVersion req]                          responseCode = rsCode res-                         size = toInteger $ L.length $ rsBody res+                         sendContentLength = rsfContentLength (rsFlags res)++                         -- don't force the bytestring if "sendContentLength"+                         -- is false, at least not if a content-length header+                         -- has been set+                         size = if not sendContentLength then+                                    maybe (toInteger $ L.length $ rsBody res)+                                          ((read . P.unpack) :: P.ByteString -> Integer)+                                          (getHeaderBS contentLengthC req)+                                  else+                                    toInteger $ L.length $ rsBody res+                          referer = B.unpack $ fromMaybe (B.pack "") $ getHeader "Referer" req                          userAgent = B.unpack $ fromMaybe (B.pack "") $ getHeader "User-Agent" req-                     log' NOTICE $ formatRequestCombined host' user time requestLine responseCode size referer userAgent-                     +                     logM "Happstack.Server.AccessLog.Combined" INFO $ formatRequestCombined host' user time requestLn responseCode size referer userAgent+                      putAugmentedResult h req res                      when (continueHTTP req res) $ rloop conf h host handler rest +-- | Unserializes the bytestring into a response.  If there is an+-- error it will return @Left msg@. parseResponse :: L.ByteString -> Either String Response parseResponse inputStr =     do (topStr,restStr) <- required "failed to separate response" $ @@ -205,7 +216,7 @@   when (rqMethod req /= HEAD) $ L.hPut h $ rsBody res   hFlush h -+-- | Serializes the request to the given handle putRequest :: Handle -> Request -> IO () putRequest h rq = do      let put = B.hPut h
src/Happstack/Server/HTTP/Socket.hs view
@@ -18,7 +18,7 @@   ) import System.IO --- alternative implementation of accept to work around EAI_AGAIN errors+-- | alternative implementation of accept to work around EAI_AGAIN errors acceptLite :: S.Socket -> IO (Handle, S.HostName, S.PortNumber) acceptLite sock = do   (sock', addr) <- S.accept sock
− src/Happstack/Server/JSON.hs
@@ -1,29 +0,0 @@-module Happstack.Server.JSON where-import Data.Char-import Data.List--data JSON = JBool Bool | JString String | JInt Int | JFloat Float | -            JObj [(String,JSON)] | JList [JSON] | JNull---jInt :: Integral a => a -> JSON-jInt = JInt . fromIntegral--class ToJSON a where-    toJSON::a->JSON-instance ToJSON JSON where toJSON=id--jsonToString :: JSON -> String-jsonToString (JBool bool) = map toLower $ show bool-jsonToString (JString string) = show string-jsonToString (JInt int) = show int-jsonToString (JFloat float) = show float-jsonToString (JObj pairs) = '{' : (concat $ (intersperse "," $ map impl pairs) )++"}"-    where-    impl (name,val) = concat [show name,":",jsonToString val]-jsonToString (JList list) = -    '[':(concat $ (intersperse "," $ map jsonToString list)) ++"]"-jsonToString JNull = "null"-type CallBack=String--data JSONCall x = JCall CallBack x
src/Happstack/Server/MessageWrap.hs view
@@ -13,7 +13,7 @@ import Happstack.Util.Common  queryInput :: SURI -> [(String, Input)]-queryInput uri = formDecode (case SURI.query $ uri of+queryInput uri = formDecode (case SURI.query uri of                                '?':r -> r                                xs    -> xs) @@ -25,7 +25,7 @@     in decodeBody ctype (getBS $ rqBody req)  --- Decodes application\/x-www-form-urlencoded inputs.      +-- | Decodes application\/x-www-form-urlencoded inputs.       formDecode :: String -> [(String, Input)] formDecode [] = [] formDecode qString = @@ -71,7 +71,7 @@               _ -> ("ERROR",simpleInput "ERROR") -- FIXME: report error     where ctype = fromMaybe defaultInputType (getContentType hs) -+-- | Packs a string into an Input of type "text/plain" simpleInput :: String -> Input simpleInput v     = Input { inputValue = L.pack v
src/Happstack/Server/Parts.hs view
@@ -8,6 +8,7 @@ import Happstack.Server.SimpleHTTP import Text.ParserCombinators.Parsec import Control.Monad+import Data.Char import Data.Maybe import Data.List import qualified Data.ByteString.Char8 as BS@@ -22,19 +23,27 @@     (FilterMonad Response m, MonadPlus m, WebMonad Response m, ServerMonad m)     => m String compressedResponseFilter = do-    mbAccept<-getHeaderM "Accept-Encoding"-    accept<- maybe mzero return mbAccept-    let eEncoding = bestEncoding $ BS.unpack accept-    (coding,action) <- case eEncoding of-        Left _ -> do+    getHeaderM "Accept-Encoding" >>=+        (maybe (return "identity") installHandler)++  where+    badEncoding = "Encoding returned not in the list of known encodings"++    installHandler accept = do+      let eEncoding = bestEncoding $ BS.unpack accept+      (coding,action) <- case eEncoding of+          Left _ -> do             setResponseCode 406             finishWith $ toResponse ""-        Right a -> return (a, maybe (fail "Encoding returned not in the list of known encodings")-            id (lookup a allEncodingHandlers))-    setHeaderM "Content-Encoding" coding-    action-    return coding +          Right a -> return (a, fromMaybe (fail badEncoding)+                                          (lookup a allEncodingHandlers))++      setHeaderM "Content-Encoding" coding+      action+      return coding++ -- | compresses the body of the response with gzip. -- does not set any headers. gzipFilter::(FilterMonad Response m) => m()@@ -113,7 +122,7 @@ --    ,compressFilter     ,deflateFilter     ,return ()-    ,fail $ "chose * as content encoding"+    ,fail "chose * as content encoding"     ]  -- | unsupported:  a parser for the Accept-Encoding header@@ -125,16 +134,17 @@             ws             char ','             ws+                 encoding1 :: GenParser Char st ([Char], Maybe Double)         encoding1 = do-            encoding <- many1 letter <|> string "*"+            encoding <- many1 alphaNum <|> string "*"             ws             quality<-optionMaybe qual             return (encoding, fmap read quality)         qual = do             char ';' >> ws >> char 'q' >> ws >> char '=' >> ws             q<-float-            return $ q+            return q         int = many1 digit         float = do                 wholePart<-many1 digit@@ -143,7 +153,7 @@             <|>                 do                 fractionalPart<-fraction-                return $ fractionalPart+                return fractionalPart         fraction :: GenParser Char st String         fraction = do             char '.'
src/Happstack/Server/SURI.hs view
@@ -38,9 +38,9 @@ unEscape = URI.unEscapeString . map (\x->if x=='+' then ' ' else x) escape = URI.escapeURIString URI.isAllowedInURI +-- | Returns true if the URI is absolute isAbs :: SURI -> Bool isAbs = not . null . URI.uriScheme . suri---isAbs = maybe True ( mbParsed  newtype SURI = SURI {suri::URI.URI} deriving (Eq,Data,Typeable) instance Show SURI where
src/Happstack/Server/SimpleHTTP.hs view
@@ -1,6 +1,16 @@-{-# LANGUAGE UndecidableInstances, OverlappingInstances, ScopedTypeVariables, FlexibleInstances, TypeSynonymInstances,-    MultiParamTypeClasses, PatternGuards, FlexibleContexts, FunctionalDependencies, GeneralizedNewtypeDeriving, PatternSignatures-    #-}++{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSignatures #-}+ {-# OPTIONS -fno-warn-orphans #-} ----------------------------------------------------------------------------- -- |@@ -16,7 +26,7 @@ -- -- By default, the built-in HTTP server will be used. However, other back-ends -- like CGI\/FastCGI can used if so desired.--- +-- -- So the general nature of 'simpleHTTP' is no different than what you'd expect -- from a web application container.  First you figure out when function is -- going to process your request, process the request to generate a response,@@ -39,12 +49,12 @@ -- suitable for sending back to the server.  Most of the time though you don't -- even need to worry about that as ServerPartT hides almost all the machinery -- for building your response by exposing a few type classes.--- +-- -- 'ServerPartT' is a pretty rich monad.  You can interact with your request, -- your response, do IO, etc.  Here is a do block that validates basic -- authentication It takes a realm name as a string, a Map of username to -- password and a server part to run if authentication fails.--- +-- -- @basicAuth'@ acts like a guard, and only produces a response when -- authentication fails.  So put it before any ServerPartT you want to demand -- authentication for in any collection of ServerPartTs.@@ -63,7 +73,7 @@ --        authHeader <- getHeaderM \"authorization\" --        case authHeader of --            Nothing -> err---            Just x  -> case parseHeader x of +--            Just x  -> case parseHeader x of --                (name, \':\':pass) | validLogin name pass -> mzero --                                   | otherwise -> err --                _                                       -> err@@ -87,7 +97,7 @@ --     when (take 2 line \/= \"ok\") $ (notfound () >> return \"refused\") --     return \"Hello World!\" -- @--- +-- -- This example will ask in the console \"return? \" if you type \"ok\" it will -- show \"Hello World!\" and if you type anything else it will return a 404. --@@ -109,6 +119,7 @@     , anyRequest     -- * WebT     , WebT(..)+    , UnWebT     , FilterFun     , Web     , mkWebT@@ -140,7 +151,7 @@     , badGateway     , internalServerError     , badRequest-    , unauthorized +    , unauthorized     , forbidden     , notFound     , seeOther@@ -203,46 +214,75 @@     , noopValidator     , lazyProcValidator     ) where-import qualified Paths_happstack_server as Cabal-import qualified Data.Version as DV-import Happstack.Server.HTTP.Client-import Happstack.Data.Xml.HaXml-import qualified Happstack.Server.MinHaXML as H  import Happstack.Server.HTTP.Types hiding (Version(..))-import qualified Happstack.Server.HTTP.Types as Types-import Happstack.Server.HTTP.Listen as Listen-import Happstack.Server.XSLT-import Happstack.Server.SURI (ToSURI)-import Happstack.Util.Common import Happstack.Server.Cookie-import Happstack.Data -- used by default implementation of fromData-import Control.Applicative-import Control.Concurrent (forkIO)-import Control.Exception (evaluate)-import Control.Monad.Reader-import Control.Monad.State-import Control.Monad.Error-import Control.Monad.Trans()-import Control.Monad.Maybe-import Control.Monad.Writer as Writer-import Data.Maybe-import Data.Monoid-import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Lazy.Char8 as L-import qualified Data.Generics as G-import qualified Data.Map as M-import Text.Html (Html,renderHtml)-import qualified Text.XHtml as XHtml (Html,renderHtml)-import qualified Happstack.Crypto.Base64 as Base64-import Data.Char-import Data.List-import System.IO-import System.Console.GetOpt-import System.Process (runInteractiveProcess, waitForProcess)-import System.Exit-import Text.Show.Functions () +import qualified Paths_happstack_server          as Cabal++import qualified Data.Version                    as DV+import Happstack.Server.HTTP.Client              (getResponse, unproxify, unrproxify)+import Happstack.Data.Xml.HaXml                  (toHaXmlEl)+import qualified Happstack.Server.MinHaXML       as H+import qualified Happstack.Server.HTTP.Listen    as Listen (listen) -- So that we can disambiguate 'Writer.listen'+import Happstack.Server.XSLT                     (XSLTCmd, XSLPath, procLBSIO)+import Happstack.Server.SURI                     (ToSURI)+import Happstack.Util.Common                     (Seconds, readM)+import Happstack.Data                            (Xml, normalize, fromPairs, Element, toXml, toPublicXml) -- used by default implementation of fromData+import Control.Applicative                       (Applicative, pure, (<*>))+import Control.Concurrent                        (forkIO)+import Control.Exception                         (evaluate)+import Control.Monad                             ( MonadPlus, mzero, mplus+                                                 , msum, ap, unless+                                                 , liftM, liftM2, liftM3, liftM4+                                                 )+import Control.Monad.Trans                       ( MonadTrans, lift+                                                 , MonadIO, liftIO+                                                 )+import Control.Monad.Reader                      ( ReaderT(ReaderT), runReaderT+                                                 , MonadReader, ask, local+                                                 , asks+                                                 )+import Control.Monad.Writer                      ( WriterT(WriterT), runWriterT+                                                 , MonadWriter, tell, pass+                                                 , listens+                                                 )+import qualified Control.Monad.Writer            as Writer (listen) -- So that we can disambiguate 'Listen.listen'+import Control.Monad.State                       (MonadState, get, put)+import Control.Monad.Error                       ( ErrorT(ErrorT), runErrorT+                                                 , Error, strMsg+                                                 , MonadError, throwError, catchError+                                                 )+import Control.Monad.Maybe                       (MaybeT(MaybeT), runMaybeT)+import Data.Char                                 (ord)+import Data.Maybe                                (fromMaybe)+import Data.Monoid                               ( Monoid, mempty, mappend+                                                 , Dual(Dual), getDual+                                                 , Endo(Endo), appEndo+                                                 )++import qualified Data.ByteString.Char8           as B+import qualified Data.ByteString.Lazy.Char8      as L+import qualified Data.ByteString.Lazy.UTF8       as LU (toString)++import qualified Data.Generics                   as G+import qualified Data.Map                        as M++import Text.Html                                 (Html, renderHtml)+import qualified Text.XHtml                      as XHtml (Html, renderHtml)++import qualified Happstack.Crypto.Base64         as Base64+import Data.Char                                 (toLower)+import Data.List                                 (isPrefixOf)+import System.IO                                 (hGetContents, hClose)+import System.Console.GetOpt                     ( OptDescr(Option)+                                                 , ArgDescr(ReqArg)+                                                 , ArgOrder(Permute)+                                                 , getOpt+                                                 )+import System.Process                            (runInteractiveProcess, waitForProcess)+import System.Exit                               (ExitCode(ExitSuccess, ExitFailure))+ -- | An alias for WebT when using IO type Web a = WebT IO a -- | An alias for using ServerPartT when using the IO@@ -279,44 +319,45 @@ -- can provide the function: -- -- @---   unpackErrorT:: (Monad m, Show e) =>---       -> (ErrorT e m) (Maybe ((Either Response a), FilterFun Response))---       -> m (Maybe ((Either Response a), FilterFun Response))+--   unpackErrorT:: (Monad m, Show e) => UnWebT (ErrorT e m) a -> UnWebT m a --   unpackErrorT handler et = do --      eitherV <- runErrorT et --      case eitherV of --          Left err -> return $ Just (Left "Catastrophic failure " ++ show e, Set $ Endo \r -> r{rsCode = 500}) --          Right x -> return x -- @--- +-- -- With @unpackErrorT@ you can now call simpleHTTP.  Just wrap your @ServerPartT@ list. -- -- @ --   simpleHTTP nullConf $ mapServerPartT unpackErrorT (myPart \`catchError\` myHandler) -- @--- +-- -- Or alternatively: -- -- @ --   simpleHTTP' unpackErrorT nullConf (myPart \`catchError\` myHandler) -- @--- +-- -- Also see 'spUnwrapErrorT' for a more sophisticated version of this function ---mapServerPartT :: (m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> ServerPartT m a -> ServerPartT n b+mapServerPartT :: (     UnWebT m a ->      UnWebT n b)+               -> (ServerPartT m a -> ServerPartT n b) mapServerPartT f ma = withRequest $ \rq -> mapWebT f (runServerPartT ma rq)  -- | A varient of mapServerPartT where the first argument, also takes a request. -- useful if you want to runServerPartT on a different ServerPartT inside your -- monad (see spUnwrapErrorT)-mapServerPartT' :: (Request -> m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> ServerPartT m a -> ServerPartT n b+mapServerPartT' :: (Request -> UnWebT m a ->      UnWebT n b)+                -> (      ServerPartT m a -> ServerPartT n b) mapServerPartT' f ma = withRequest $ \rq -> mapWebT (f rq) (runServerPartT ma rq)+ instance MonadTrans (ServerPartT) where     lift m = withRequest (\_ -> lift m) -instance (Monad m) => Monoid (ServerPartT m a)- where mempty = mzero-       mappend = mplus+instance (Monad m) => Monoid (ServerPartT m a) where+    mempty  = mzero+    mappend = mplus  instance (Monad m, Functor m) => Applicative (ServerPartT m) where     pure = return@@ -351,48 +392,58 @@ -- you can add a ReaderT to your monad stack without -- any trouble. class Monad m => ServerMonad m where-    askRq :: m Request-    localRq :: (Request->Request)->m a->m a+    askRq   :: m Request+    localRq :: (Request -> Request) -> m a -> m a  instance (Monad m) => ServerMonad (ServerPartT m) where     askRq = ServerPartT $ ask     localRq f m = ServerPartT $ local f (unServerPartT m)+ ------------------------------- -- HERE BEGINS WebT definitions  -- | A monoid operation container. -- If a is a monoid, then SetAppend is a monoid with the following behaviors:--- +-- -- @---   Set x `mappend` Append y = Set (x `mappend` y)+--   Set    x `mappend` Append y = Set    (x `mappend` y) --   Append x `mappend` Append y = Append (x `mappend` y)---   \_     `mappend` Set y = Set y+--   \_       `mappend` Set y    = Set y -- @ ----- A simple way of sumerizing this is, if the left side is Append, then the--- right is appended to the left.  If the left side is Set, then the right side+-- A simple way of sumerizing this is, if the right side is Append, then the+-- right is appended to the left.  If the right side is Set, then the right side -- is ignored.+ data SetAppend a = Set a | Append a     deriving (Eq, Show)+ instance Monoid a => Monoid (SetAppend a) where    mempty = Append mempty-   Set x    `mappend` Append y = Set (x `mappend` y)++   Set    x `mappend` Append y = Set    (x `mappend` y)    Append x `mappend` Append y = Append (x `mappend` y)    _        `mappend` Set y    = Set y -value :: SetAppend t -> t-value (Set x) = x-value (Append x) = x+-- | Extract the value from a SetAppend+-- Note that a SetAppend is actually a CoPointed from:+-- <http://hackage.haskell.org/packages/archive/category-extras/latest/doc/html/Control-Functor-Pointed.html>+-- But lets not drag in that dependency. yet...+extract :: SetAppend t -> t+extract (Set    x) = x+extract (Append x) = x  instance Functor (SetAppend) where-    fmap f (Set x) = Set $ f x+    fmap f (Set    x) = Set    $ f x     fmap f (Append x) = Append $ f x  -- | @FilterFun@ is a lot more fun to type than @SetAppend (Dual (Endo a))@ type FilterFun a = SetAppend (Dual (Endo a)) -newtype FilterT a m b =-   FilterT { unFilterT :: WriterT (FilterFun a) m b }+unFilterFun :: FilterFun a -> (a -> a)+unFilterFun = appEndo . getDual . extract++newtype FilterT a m b = FilterT { unFilterT :: WriterT (FilterFun a) m b }    deriving (Monad, MonadTrans, Functor, MonadIO)  -- | A set of functions for manipulating filters.  A ServerPartT implements@@ -420,23 +471,27 @@     -- existing filter function.     composeFilter :: (a->a) -> m ()     -- | retrives the filter from the environment-    getFilter :: m b -> m (b,a->a)+    getFilter :: m b -> m (b, a->a)  instance (Monad m) => FilterMonad a (FilterT a m) where-    setFilter f = FilterT $ Writer.tell $ Set $ Dual $ Endo f-    composeFilter f = FilterT $ Writer.tell $ Append $ Dual $ Endo f-    getFilter m = FilterT $ Writer.listens (appEndo . getDual . value)  (unFilterT m) +    setFilter     = FilterT . tell                . Set    . Dual . Endo+    composeFilter = FilterT . tell                . Append . Dual . Endo+    getFilter     = FilterT . listens unFilterFun . unFilterT  -- | The basic response building object.+newtype WebT m a = WebT { unWebT :: ErrorT Response (FilterT (Response) (MaybeT m)) a }+    deriving (MonadIO, Functor)++-- | --  It is worth discussing the unpacked structure of WebT a bit as it's exposed --  in 'mapServerPartT' and 'mapWebT'. -- --  A fully unpacked WebT has a structure that looks like:---  +-- --  @ --    ununWebT $ WebT m a :: m (Maybe (Either Response a, FilterFun Response)) --  @---   +-- --  So, ignoring m, as it is just the containing Monad, the outermost layer is --  a Maybe.  This is 'Nothing' if 'mzero' was called or @Just (Either Response --  a, SetAppend (Endo Response))@ if 'mzero' wasn't called.  Inside the Maybe,@@ -475,9 +530,8 @@ --          Just (Right a, f) -> Just (Right a, f) -- a is our normal monadic value --                                                 -- f is still our filter function --  @---  -newtype WebT m a = WebT { unWebT :: ErrorT Response (FilterT (Response) (MaybeT m)) a }-    deriving (MonadIO, Functor)+--+type UnWebT m a = m (Maybe (Either Response a, FilterFun Response))  instance Monad m => Monad (WebT m) where     m >>= f = WebT $ unWebT m >>= unWebT . f@@ -525,39 +579,36 @@ instance (Monad m) => FilterMonad Response (WebT m) where     setFilter f = WebT $ lift $ setFilter $ f     composeFilter f = WebT . lift . composeFilter $ f-    getFilter m = WebT $ ErrorT $ getFilter (runErrorT $ unWebT m) >>= liftWebT-        where liftWebT (Left r, _) = return $ Left r-              liftWebT (Right a, f) = return $ Right (a, f)+    getFilter     m = WebT $ ErrorT $ fmap lft $ getFilter (runErrorT $ unWebT m)+        where+          lft (Left  r, _) = Left r+          lft (Right a, f) = Right (a, f)  instance (Monad m) => Monoid (WebT m a) where     mempty = mzero     mappend = mplus --- | takes your WebT, converts the monadic value to a Response,--- applys your filter, and returns it wrapped in a Maybe.-runWebT :: (ToMessage b, Monad m) => WebT m b -> m (Maybe Response)-runWebT m = runMaybeT $ do-                (r,ed) <- runWriterT $ unFilterT $ runErrorT $ unWebT $ m-                let f = appEndo $ getDual $ value ed-                return $ either (f) (f . toResponse) r+-- | takes your WebT, if it is 'mempty' it returns Nothing else it+-- converts the value to a Response and applies your filter to it.+runWebT :: forall m b. (Functor m, ToMessage b) => WebT m b -> m (Maybe Response)+runWebT = (fmap . fmap) appFilterToResp . ununWebT+    where+      appFilterToResp :: (Either Response b, FilterFun Response) -> Response+      appFilterToResp (e, ff) = unFilterFun ff $ either id toResponse e+ -- | for when you really need to unpack a WebT entirely (and not -- just unwrap the first layer with unWebT)-ununWebT :: WebT m a-    -> m (Maybe-            (Either Response a,-             FilterFun Response))+ununWebT :: WebT m a -> UnWebT m a ununWebT = runMaybeT . runWriterT . unFilterT . runErrorT . unWebT  -- | for wrapping a WebT back up.  @mkWebT . ununWebT = id@-mkWebT :: m (Maybe-       (Either Response a,-        FilterFun Response)) -> WebT m a+mkWebT :: UnWebT m a -> WebT m a mkWebT = WebT . ErrorT . FilterT . WriterT . MaybeT  -- | see 'mapServerPartT' for a discussion of this function-mapWebT :: (m (Maybe (Either Response a, FilterFun Response)) -> n (Maybe (Either Response b, FilterFun Response))) -> WebT m a -> WebT n b-mapWebT f ma = mkWebT $  f (ununWebT ma)-+mapWebT :: (UnWebT m a -> UnWebT n b)+        -> (  WebT m a ->   WebT n b)+mapWebT f ma = mkWebT $ f (ununWebT ma)  instance (Monad m, Functor m) => Applicative (WebT m) where     pure = return@@ -576,7 +627,7 @@  	catchError action handler = mkWebT $ catchError (ununWebT action) (ununWebT . handler)  instance MonadWriter w m => MonadWriter w (WebT m) where-    tell = lift . Writer.tell+    tell = lift . tell     listen m = mkWebT $ Writer.listen (ununWebT m) >>= (return . liftWebT)         where liftWebT (Nothing, _) = Nothing               liftWebT (Just (Left x,f), _) = Just (Left x,f)@@ -627,36 +678,39 @@  -- | a combination of simpleHTTP and 'mapServerPartT'.  See 'mapServerPartT' for a discussion -- of the first argument of this function.-simpleHTTP' :: (Monad m, ToMessage b) =>-   (m (Maybe (Either Response a, FilterFun Response))-   -> IO (Maybe (Either Response b, FilterFun Response)))-   -> Conf-   -> ServerPartT m a-   -> IO ()-simpleHTTP' toIO conf hs = do+simpleHTTP' :: (ToMessage b, Monad m, Functor m) => (UnWebT m a -> UnWebT IO b)+            -> Conf -> ServerPartT m a -> IO ()+simpleHTTP' toIO conf hs =     Listen.listen conf (\req -> runValidator (fromMaybe return (validator conf)) =<< (simpleHTTP'' (mapServerPartT toIO hs) req))-     + -- | Generate a result from a 'ServerPart' and a 'Request'. This is mainly used -- by CGI (and fast-cgi) wrappers.-simpleHTTP'' :: (ToMessage b, Monad m) => ServerPartT m b -> Request -> m Response+simpleHTTP'' :: (ToMessage b, Monad m, Functor m) => ServerPartT m b -> Request -> m Response simpleHTTP'' hs req =  (runWebT $ runServerPartT hs req) >>= (return . (maybe standardNotFound id))     where         standardNotFound = setHeader "Content-Type" "text/html" $ toResponse notFoundHtml  --- | a wrapper for Read apparently.  Pretty much only used for 'path' and probably--- unnecessarily, as it is exactly "readM"+-- | This class is used by 'path' to parse a path component into a value.+-- At present, the instances for number types (Int, Float, etc) just+-- call 'readM'. The instance for 'String' however, just passes the+-- path component straight through. This is so that you can read a+-- path component which looks like this as a String:+--+--  \/somestring\/+--+-- instead of requiring the path component to look like:+--+-- \/"somestring"\/ class FromReqURI a where     fromReqURI :: String -> Maybe a ---instance FromReqURI String where fromReqURI = Just-instance FromReqURI Int where    fromReqURI = readM-instance FromReqURI Integer where    fromReqURI = readM-instance FromReqURI Float where  fromReqURI = readM-instance FromReqURI Double where fromReqURI = readM+instance FromReqURI String  where fromReqURI = Just+instance FromReqURI Int     where fromReqURI = readM+instance FromReqURI Integer where fromReqURI = readM+instance FromReqURI Float   where fromReqURI = readM+instance FromReqURI Double  where fromReqURI = readM  type RqData a = ReaderT ([(String,Input)], [(String,Cookie)]) Maybe a @@ -739,9 +793,9 @@   class MatchMethod m where matchMethod :: m -> Method -> Bool-instance MatchMethod Method where matchMethod m = (== m) +instance MatchMethod Method where matchMethod m = (== m) instance MatchMethod [Method] where matchMethod methods = (`elem` methods)-instance MatchMethod (Method -> Bool) where matchMethod f = f +instance MatchMethod (Method -> Bool) where matchMethod f = f instance MatchMethod () where matchMethod () _ = True  -- | flatten turns your arbitrary @m a@ and converts it too@@ -768,7 +822,7 @@ addHeaderM a v = composeFilter $ \res-> addHeader a v res  -- | sets a header into the response.  This will replace--- an existing header of the same name.  Use addHeaderM, if you +-- an existing header of the same name.  Use addHeaderM, if you -- want to add more than one header of the same name. setHeaderM :: (FilterMonad Response m) => String -> String -> m () setHeaderM a v = composeFilter $ \res -> setHeader a v res@@ -779,7 +833,7 @@ guardRq :: (ServerMonad m, MonadPlus m) => (Request -> Bool) -> m () guardRq f = do     rq <- askRq-    when ( f rq /= True ) mzero +    unless (f rq) mzero  -- | Guard against the method.  This function also guards against -- any remaining path segments.  See methodOnly for the version@@ -814,19 +868,15 @@         rq <- askRq         case rqPaths rq of             (p:xs) | p == staticPath -> localRq (\newRq -> newRq{rqPaths = xs}) handle-                   | otherwise -> mzero             _ -> mzero --- | Pop a path element and parse it.  Annoyingly enough, rather than just using Read--- (or providing a parser argument), this method uses 'FromReqURI' which is just a wrapper--- for Read.+-- | Pop a path element and parse it using the 'fromReqURI' in the 'FromReqURI' class. path :: (FromReqURI a, MonadPlus m, ServerMonad m) => (a -> m b) -> m b path handle = do     rq <- askRq     case rqPaths rq of         (p:xs) | Just a <- fromReqURI p                             -> localRq (\newRq -> newRq{rqPaths = xs}) (handle a)-               | otherwise -> mzero         _ -> mzero  -- | grabs the rest of the URL (dirs + query) and passes it to your handler@@ -892,11 +942,7 @@ -- | withDataFn is like with data, but you pass in a RqData monad -- for reading. withDataFn :: (MonadPlus m, ServerMonad m) => RqData a -> (a -> m r) -> m r-withDataFn fn handle = do-    d <- getDataFn fn-    case d of-        Nothing -> mzero-        Just a -> handle a+withDataFn fn handle = getDataFn fn >>= maybe mzero handle  -- | proxyServe is for creating ServerPartT's that proxy. -- The sole argument [String] is a list of allowed domains for@@ -923,7 +969,7 @@      | superdomain `elem` wildcards =True      | otherwise = False      where-     domain = head (rqPaths rq) +     domain = head (rqPaths rq)      superdomain = tail $ snd $ break (=='.') domain      wildcards = (map (drop 2) $ filter ("*." `isPrefixOf`) allowed) @@ -980,9 +1026,9 @@  doXslt :: (MonadIO m) =>           XSLTCmd -> XSLPath -> Response -> m Response-doXslt cmd xslPath res = +doXslt cmd xslPath res =     do new <- liftIO $ procLBSIO cmd xslPath $ rsBody res-       return $ setHeader "Content-Type" "text/html" $ +       return $ setHeader "Content-Type" "text/html" $               setHeader "Content-Length" (show $ L.length new) $               res { rsBody = new } @@ -1012,9 +1058,11 @@ ok :: (FilterMonad Response m) => a -> m a ok = resp 200 +-- | Respond with @500 Interal Server Error@ internalServerError :: (FilterMonad Response m) => a -> m a internalServerError = resp 500 +-- | Responds with @502 Bad Gateway@ badGateway :: (FilterMonad Response m) => a -> m a badGateway = resp 502 @@ -1072,7 +1120,7 @@ anyRequest x = withRequest $ \_ -> x  -- | again, why is this useful?-applyRequest :: (ToMessage a, Monad m) =>+applyRequest :: (ToMessage a, Monad m, Functor m) =>                 ServerPartT m a -> Request -> Either (m Response) b applyRequest hs = simpleHTTP'' hs >>= return . Left @@ -1120,7 +1168,7 @@  -- | Gets the named input as a String look :: String -> RqData String-look = fmap L.unpack . lookBS+look = fmap LU.toString . lookBS  -- | Gets the named cookie -- the cookie name is case insensitive@@ -1145,7 +1193,7 @@  -- | gets all the input parameters, and converts them to a string lookPairs :: RqData [(String,String)]-lookPairs = asks fst >>= return . map (\(n,vbs)->(n,L.unpack $ inputValue vbs))+lookPairs = asks fst >>= return . map (\(n,vbs)->(n,LU.toString $ inputValue vbs))   --------------------------------------------------------------@@ -1157,7 +1205,7 @@ -- --   You can wrap the complete second argument to 'simpleHTTP' in this function. ---errorHandlerSP :: (Monad m, Error e) => (Request -> e -> WebT m a) -> ServerPartT (ErrorT e m) a -> ServerPartT m a +errorHandlerSP :: (Monad m, Error e) => (Request -> e -> WebT m a) -> ServerPartT (ErrorT e m) a -> ServerPartT m a errorHandlerSP handler sps = withRequest $ \req -> mkWebT $ do 			eer <- runErrorT $ ununWebT $ runServerPartT sps req 			case eer of@@ -1179,18 +1227,17 @@ -- that monad into a ServerPartT m a.  Used with -- mapServerPartT\' to allow throwError and catchError inside your -- monad.  Eg.--- +-- -- @ --   simpleHTTP conf $ mapServerPartT\' (spUnWrapErrorT failurePart)  $ myPart \`catchError\` errorPart -- @--- +-- -- Note that @failurePart@ will only be run if errorPart threw an error -- so it doesn\'t have to be very complex.-spUnwrapErrorT:: Monad m =>-        (e -> ServerPartT m a)-        -> Request-        -> ErrorT e m (Maybe (Either Response a, FilterFun Response))-        -> m (Maybe (Either Response a, FilterFun Response))+spUnwrapErrorT:: Monad m => (e -> ServerPartT m a)+              -> Request+              -> UnWebT (ErrorT e m) a+              -> UnWebT m a spUnwrapErrorT handler rq = \x -> do     err <- runErrorT x     case err of@@ -1245,9 +1292,9 @@ validateConf = nullConf { validator = Just wdgHTMLValidator }  -- |Actually perform the validation on a 'Response'--- +-- -- Run the validator specified in the 'Response'. If none is provide--- use the supplied default instead. +-- use the supplied default instead. -- -- Note: This function will run validation unconditionally. You -- probably want 'setValidator' or 'validateConf'.@@ -1278,7 +1325,7 @@ noopValidator = return  -- |Validate the 'Response' using an external application.--- +-- -- If the external application returns 0, the original response is -- returned unmodified. If the external application returns non-zero, a 'Response' -- containing the error messages and original response body is@@ -1290,7 +1337,7 @@ -- -- NOTE: This function requirse the use of -threaded to avoid blocking. -- However, you probably need that for Happstack anyway.--- +-- -- See also: 'wdgHTMLValidator' lazyProcValidator :: FilePath -- ^ name of executable                -> [String] -- ^ arguements to pass to the executable@@ -1311,14 +1358,14 @@            ec <- waitForProcess ph            case ec of              ExitSuccess     -> return response-             (ExitFailure _) -> +             (ExitFailure _) ->                  return $ toResponse (unlines ([ "ExitCode: " ++ show ec                                                , "stdout:"                                                , out                                                , "stderr:"                                                , err                                                , "input:"-                                               ] ++ +                                               ] ++                                                showLines (rsBody response)))     | otherwise = return response     where@@ -1335,21 +1382,37 @@     finishWith $ res  failHtml:: String->String-failHtml errString = "<html><head><title>Happstack "-    ++ ver ++ " Internal Server Error</title>"+failHtml errString = +   "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"+    ++ "<html><head><title>Happstack "+    ++ ver ++ " Internal Server Error</title></head>"     ++ "<body><h1>Happstack " ++ ver ++ "</h1>"-    ++ "<p>Something went wrong here<br />"-    ++ "Internal server error<br />"+    ++ "<p>Something went wrong here<br>"+    ++ "Internal server error<br>"     ++ "Everything has stopped</p>"-    ++ "<p>The error was \"" ++ errString ++ "\"</p></body></html>"+    ++ "<p>The error was \"" ++ (escapeString errString) ++ "\"</p></body></html>"     where ver = DV.showVersion Cabal.version +escapeString :: String -> String+escapeString str = concatMap encodeEntity str+    where+      encodeEntity :: Char -> String+      encodeEntity '<' = "&lt;"+      encodeEntity '>' = "&gt;"+      encodeEntity '&' = "&amp;"+      encodeEntity '"' = "&quot;"+      encodeEntity c+          | ord c > 127 = "&#" ++ show (ord c) ++ ";"+          | otherwise = [c]+ notFoundHtml :: String-notFoundHtml = "<html><head><title>Happstack "-    ++ ver ++ " File not found</title>"+notFoundHtml = +    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"+    ++ "<html><head><title>Happstack "+    ++ ver ++ " File not found</title></head>"     ++ "<body><h1>Happstack " ++ ver ++ "</h1>"-    ++ "<p>Your file is not found<br />"-    ++ "To try again is useless<br />"+    ++ "<p>Your file is not found<br>"+    ++ "To try again is useless<br>"     ++ "It is just not here</p>"     ++ "</body></html>"     where ver = DV.showVersion Cabal.version