Integrated some external packages; web-encodings 0.2.0
This commit is contained in:
parent
0a0e7e8f8a
commit
9483f6037e
95
Data/Object/Translate.hs
Normal file
95
Data/Object/Translate.hs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
---------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- Module : Data.Object.Translate
|
||||||
|
-- Copyright : Michael Snoyman
|
||||||
|
-- License : BSD3
|
||||||
|
--
|
||||||
|
-- Maintainer : Michael Snoyman <michael@snoyman.com>
|
||||||
|
-- Stability : Stable
|
||||||
|
-- Portability : portable
|
||||||
|
--
|
||||||
|
-- Objects which can be translated into different languages.
|
||||||
|
---------------------------------------------------------
|
||||||
|
module Data.Object.Translate
|
||||||
|
( -- * Types
|
||||||
|
Language
|
||||||
|
, TranslatedString
|
||||||
|
, Translator
|
||||||
|
, TranslateObject
|
||||||
|
, TranslateKeyObject
|
||||||
|
-- * Type classes
|
||||||
|
, CanTranslate (..)
|
||||||
|
-- * Utilities for objects
|
||||||
|
, translateObject
|
||||||
|
, translateKeyObject
|
||||||
|
-- * Specialized functions
|
||||||
|
, toTranslateObject
|
||||||
|
, fromTranslateObject
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Object
|
||||||
|
import Data.Attempt
|
||||||
|
import Data.Object.Text
|
||||||
|
|
||||||
|
-- | Should usually be the well established I18N translation code. Examples
|
||||||
|
-- include en, en_US, es, and so on. If you use these common codes, you will
|
||||||
|
-- have easy interop with other systems.
|
||||||
|
type Language = String
|
||||||
|
type TranslatedString = Text
|
||||||
|
|
||||||
|
-- | Given a list of destination languages (ordered by preference), generate
|
||||||
|
-- a translated string. Must return some value.
|
||||||
|
type Translator = [Language] -> TranslatedString
|
||||||
|
|
||||||
|
-- | Usually you do not need to translate both keys and values, so this should
|
||||||
|
-- be the more common type.
|
||||||
|
type TranslateObject = Object Text Translator
|
||||||
|
|
||||||
|
-- | For the occassions when you really need to translate everything.
|
||||||
|
type TranslateKeyObject = Object Translator Translator
|
||||||
|
|
||||||
|
-- | Anything which can be translated into a different language.
|
||||||
|
--
|
||||||
|
-- Minimal complete definition: translate or (tryTranslate and
|
||||||
|
-- defaultTranslate).
|
||||||
|
class CanTranslate a where
|
||||||
|
translate :: a -> Translator
|
||||||
|
translate a [] = defaultTranslate a
|
||||||
|
translate a (lang:langs) =
|
||||||
|
fromMaybe (translate a langs) $ tryTranslate a lang
|
||||||
|
|
||||||
|
tryTranslate :: a -> Language -> Maybe TranslatedString
|
||||||
|
tryTranslate a = Just . translate a . return
|
||||||
|
|
||||||
|
defaultTranslate :: a -> TranslatedString
|
||||||
|
defaultTranslate a = translate a []
|
||||||
|
|
||||||
|
instance CanTranslate Text where
|
||||||
|
translate = const
|
||||||
|
|
||||||
|
-- | Generate a 'TextObject' with the translation of the
|
||||||
|
-- original based on the language list supplied.
|
||||||
|
translateObject :: [Language]
|
||||||
|
-> TranslateObject
|
||||||
|
-> TextObject
|
||||||
|
translateObject langs = fmap ($ langs)
|
||||||
|
|
||||||
|
-- | Same as 'translateObject', but translate the keys as well as the values.
|
||||||
|
translateKeyObject :: [Language]
|
||||||
|
-> TranslateKeyObject
|
||||||
|
-> TextObject
|
||||||
|
translateKeyObject langs = mapKeysValues ($ langs) ($ langs)
|
||||||
|
|
||||||
|
-- | 'toObject' specialized for 'TranslateObject's
|
||||||
|
toTranslateObject :: ToObject a TranslatedString Translator
|
||||||
|
=> a -> TranslateObject
|
||||||
|
toTranslateObject = toObject
|
||||||
|
|
||||||
|
-- | 'fromObject' specialized for 'TranslateObject's
|
||||||
|
fromTranslateObject :: FromObject a TranslatedString Translator
|
||||||
|
=> TranslateObject
|
||||||
|
-> Attempt a
|
||||||
|
fromTranslateObject = fromObject
|
||||||
61
Hack/Middleware/CleanPath.hs
Normal file
61
Hack/Middleware/CleanPath.hs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
module Hack.Middleware.CleanPath (cleanPath, splitPath) where
|
||||||
|
|
||||||
|
import Hack
|
||||||
|
import qualified Data.ByteString.Lazy as BS
|
||||||
|
import Data.List
|
||||||
|
import Web.Encodings
|
||||||
|
import Data.List.Split
|
||||||
|
|
||||||
|
-- | Performs redirects as per 'splitPath'.
|
||||||
|
cleanPath :: Middleware
|
||||||
|
cleanPath app env =
|
||||||
|
case splitPath $ pathInfo env of
|
||||||
|
Left p -> do
|
||||||
|
-- include the query string if there
|
||||||
|
let suffix =
|
||||||
|
case queryString env of
|
||||||
|
"" -> ""
|
||||||
|
q@('?':_) -> q
|
||||||
|
q -> '?' : q
|
||||||
|
return $! Response 303 [("Location", p ++ suffix)] BS.empty
|
||||||
|
Right _ -> app env
|
||||||
|
|
||||||
|
-- | Given a certain requested path, return either a corrected path
|
||||||
|
-- to redirect to or the tokenized path.
|
||||||
|
--
|
||||||
|
-- This code corrects for the following issues:
|
||||||
|
--
|
||||||
|
-- * It is missing a trailing slash, and there is no period after the
|
||||||
|
-- last slash.
|
||||||
|
--
|
||||||
|
-- * There are any doubled slashes.
|
||||||
|
splitPath :: String -> Either String [String]
|
||||||
|
splitPath s =
|
||||||
|
let corrected = ats $ rds s
|
||||||
|
in if corrected == s
|
||||||
|
then Right $ map decodeUrl $ filter (\l -> length l /= 0)
|
||||||
|
$ splitOneOf "/" s
|
||||||
|
else Left corrected
|
||||||
|
|
||||||
|
-- | Remove double slashes
|
||||||
|
rds :: String -> String
|
||||||
|
rds [] = []
|
||||||
|
rds [x] = [x]
|
||||||
|
rds (a:b:c)
|
||||||
|
| a == '/' && b == '/' = rds (b:c)
|
||||||
|
| otherwise = a : rds (b:c)
|
||||||
|
|
||||||
|
-- | Add a trailing slash if it is missing. Empty string is left alone.
|
||||||
|
ats :: String -> String
|
||||||
|
ats [] = []
|
||||||
|
ats s =
|
||||||
|
if last s == '/' || dbs (reverse s)
|
||||||
|
then s
|
||||||
|
else s ++ "/"
|
||||||
|
|
||||||
|
-- | Is there a period before a slash here?
|
||||||
|
dbs :: String -> Bool
|
||||||
|
dbs ('/':_) = False
|
||||||
|
dbs ('.':_) = True
|
||||||
|
dbs (_:x) = dbs x
|
||||||
|
dbs [] = False
|
||||||
118
Hack/Middleware/ClientSession.hs
Normal file
118
Hack/Middleware/ClientSession.hs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
module Hack.Middleware.ClientSession
|
||||||
|
( clientsession
|
||||||
|
-- * Generating keys
|
||||||
|
, Word256
|
||||||
|
, defaultKeyFile
|
||||||
|
, getKey
|
||||||
|
, getDefaultKey
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Prelude hiding (exp)
|
||||||
|
import Hack
|
||||||
|
import Web.Encodings
|
||||||
|
import Data.List (partition, intercalate)
|
||||||
|
import Data.Function.Predicate (is, isn't, equals)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Web.ClientSession
|
||||||
|
import Data.Time.Clock (getCurrentTime, UTCTime, addUTCTime)
|
||||||
|
import Data.Time.LocalTime () -- Show instance of UTCTime
|
||||||
|
import Data.Time.Format (formatTime) -- Read instance of UTCTime
|
||||||
|
import System.Locale (defaultTimeLocale)
|
||||||
|
import Control.Monad (guard)
|
||||||
|
|
||||||
|
-- | Automatic encrypting and decrypting of client session data.
|
||||||
|
--
|
||||||
|
-- Using the clientsession package, this middleware handles automatic
|
||||||
|
-- encryption, decryption, checking, expiration and renewal of whichever
|
||||||
|
-- cookies you ask it to. For example, if you tell it to deal with the
|
||||||
|
-- cookie \"IDENTIFIER\", it will do the following:
|
||||||
|
--
|
||||||
|
-- * When you specify an \"IDENTIFIER\" value in your 'Response', it will
|
||||||
|
-- encrypt the value, along with the session expiration date and the
|
||||||
|
-- REMOTE_HOST of the user. It will then be set as a cookie on the client.
|
||||||
|
--
|
||||||
|
-- * When there is an incoming \"IDENTIFIER\" cookie from the user, it will
|
||||||
|
-- decrypt it and check both the expiration date and the REMOTE_HOST. If
|
||||||
|
-- everything matches up, it will set the \"IDENTIFIER\" value in
|
||||||
|
-- 'hackHeaders'.
|
||||||
|
--
|
||||||
|
-- * If the client sent an \"IDENTIFIER\" and the application does not set
|
||||||
|
-- a new value, this will reset the cookie to a new expiration date. This
|
||||||
|
-- way, you do not have sessions timing out every 20 minutes.
|
||||||
|
--
|
||||||
|
-- As far as security: clientsesion itself handles hashing and encrypting
|
||||||
|
-- the data to make sure that the user can neither see not tamper with it.
|
||||||
|
clientsession :: [String] -- ^ list of cookies to intercept
|
||||||
|
-> Word256 -- ^ encryption key
|
||||||
|
-> Middleware
|
||||||
|
clientsession cnames key app env = do
|
||||||
|
let initCookiesRaw :: String
|
||||||
|
initCookiesRaw = fromMaybe "" $ lookup "Cookie" $ http env
|
||||||
|
nonCookies :: [(String, String)]
|
||||||
|
nonCookies = filter (fst `isn't` (== "Cookie")) $ http env
|
||||||
|
initCookies :: [(String, String)]
|
||||||
|
initCookies = decodeCookies initCookiesRaw
|
||||||
|
cookies, interceptCookies :: [(String, String)]
|
||||||
|
(interceptCookies, cookies) = partition (fst `is` (`elem` cnames))
|
||||||
|
initCookies
|
||||||
|
cookiesRaw :: String
|
||||||
|
cookiesRaw = intercalate "; " $ map (\(k, v) -> k ++ "=" ++ v)
|
||||||
|
cookies
|
||||||
|
remoteHost :: String
|
||||||
|
remoteHost = fromMaybe "" $ lookup "REMOTE_HOST" $ http env
|
||||||
|
now <- getCurrentTime
|
||||||
|
let convertedCookies =
|
||||||
|
takeJusts $
|
||||||
|
map (decodeCookie key now remoteHost) interceptCookies
|
||||||
|
let env' = env { http = ("Cookie", cookiesRaw)
|
||||||
|
: filter (fst `equals` "Cookie") (http env)
|
||||||
|
++ nonCookies
|
||||||
|
, hackHeaders = hackHeaders env ++ convertedCookies
|
||||||
|
}
|
||||||
|
res <- app env'
|
||||||
|
let (interceptHeaders, headers') = partition (fst `is` (`elem` cnames))
|
||||||
|
$ headers res
|
||||||
|
let twentyMinutes :: Int
|
||||||
|
twentyMinutes = 20 * 60
|
||||||
|
let exp = fromIntegral twentyMinutes `addUTCTime` now
|
||||||
|
let formattedExp = formatTime defaultTimeLocale "%a, %d-%b-%Y %X %Z" exp
|
||||||
|
let oldCookies = filter (\(k, _) -> not $ k `elem` map fst interceptHeaders) convertedCookies
|
||||||
|
let newCookies = map (setCookie key exp formattedExp remoteHost) $
|
||||||
|
oldCookies ++ interceptHeaders
|
||||||
|
let res' = res { headers = newCookies ++ headers' }
|
||||||
|
return res'
|
||||||
|
|
||||||
|
takeJusts :: [Maybe a] -> [a]
|
||||||
|
takeJusts [] = []
|
||||||
|
takeJusts (Just x:rest) = x : takeJusts rest
|
||||||
|
takeJusts (Nothing:rest) = takeJusts rest
|
||||||
|
|
||||||
|
setCookie :: Word256
|
||||||
|
-> UTCTime -- ^ expiration time
|
||||||
|
-> String -- ^ formatted expiration time
|
||||||
|
-> String -- ^ remote host
|
||||||
|
-> (String, String) -> (String, String)
|
||||||
|
setCookie key exp fexp rhost (cname, cval) =
|
||||||
|
("Set-Cookie", cname ++ "=" ++ val ++ "; path=/; expires=" ++ fexp)
|
||||||
|
where
|
||||||
|
val = encrypt key $ show $ Cookie exp rhost cval
|
||||||
|
|
||||||
|
data Cookie = Cookie UTCTime String String deriving (Show, Read)
|
||||||
|
decodeCookie :: Word256 -- ^ key
|
||||||
|
-> UTCTime -- ^ current time
|
||||||
|
-> String -- ^ remote host field
|
||||||
|
-> (String, String) -- ^ cookie pair
|
||||||
|
-> Maybe (String, String)
|
||||||
|
decodeCookie key now rhost (cname, encrypted) = do
|
||||||
|
decrypted <- decrypt key encrypted
|
||||||
|
(Cookie exp rhost' val) <- mread decrypted
|
||||||
|
guard $ exp > now
|
||||||
|
guard $ rhost' == rhost
|
||||||
|
guard $ val /= ""
|
||||||
|
return (cname, val)
|
||||||
|
|
||||||
|
mread :: (Monad m, Read a) => String -> m a
|
||||||
|
mread s =
|
||||||
|
case reads s of
|
||||||
|
[] -> fail $ "Reading of " ++ s ++ " failed"
|
||||||
|
((x, _):_) -> return x
|
||||||
44
Hack/Middleware/Gzip.hs
Normal file
44
Hack/Middleware/Gzip.hs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
---------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : Hack.Middleware.Gzip
|
||||||
|
-- Copyright : Michael Snoyman
|
||||||
|
-- License : BSD3
|
||||||
|
--
|
||||||
|
-- Maintainer : Michael Snoyman <michael@snoyman.com>
|
||||||
|
-- Stability : Unstable
|
||||||
|
-- Portability : portable
|
||||||
|
--
|
||||||
|
-- Automatic gzip compression of responses.
|
||||||
|
--
|
||||||
|
---------------------------------------------------------
|
||||||
|
module Hack.Middleware.Gzip (gzip) where
|
||||||
|
|
||||||
|
import Hack
|
||||||
|
import Codec.Compression.GZip (compress)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.List.Split (splitOneOf)
|
||||||
|
|
||||||
|
-- | Use gzip to compress the body of the response.
|
||||||
|
--
|
||||||
|
-- Analyzes the \"Accept-Encoding\" header from the client to determine
|
||||||
|
-- if gzip is supported.
|
||||||
|
--
|
||||||
|
-- Possible future enhancements:
|
||||||
|
--
|
||||||
|
-- * Only compress if the response is above a certain size.
|
||||||
|
--
|
||||||
|
-- * Add Content-Length.
|
||||||
|
--
|
||||||
|
-- * I read somewhere that \"the beast\" (MSIE) can\'t support compression
|
||||||
|
-- for Javascript files..
|
||||||
|
gzip :: Middleware
|
||||||
|
gzip app env = do
|
||||||
|
res <- app env
|
||||||
|
let enc = fromMaybe [] $ splitOneOf "," `fmap` lookup "Accept-Encoding"
|
||||||
|
(http env)
|
||||||
|
if "gzip" `elem` enc
|
||||||
|
then return res
|
||||||
|
{ body = compress $ body res
|
||||||
|
, headers = ("Content-Encoding", "gzip") : headers res
|
||||||
|
}
|
||||||
|
else return res
|
||||||
66
Hack/Middleware/Jsonp.hs
Normal file
66
Hack/Middleware/Jsonp.hs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
---------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : Hack.Middleware.Jsonp
|
||||||
|
-- Copyright : Michael Snoyman
|
||||||
|
-- License : BSD3
|
||||||
|
--
|
||||||
|
-- Maintainer : Michael Snoyman <michael@snoyman.com>
|
||||||
|
-- Stability : Unstable
|
||||||
|
-- Portability : portable
|
||||||
|
--
|
||||||
|
-- Automatic wrapping of JSON responses to convert into JSONP.
|
||||||
|
--
|
||||||
|
---------------------------------------------------------
|
||||||
|
module Hack.Middleware.Jsonp (jsonp) where
|
||||||
|
|
||||||
|
import Hack
|
||||||
|
import Web.Encodings (decodeUrlPairs)
|
||||||
|
import qualified Data.ByteString.Lazy.Char8 as B8
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.List (isInfixOf)
|
||||||
|
|
||||||
|
-- | Wrap json responses in a jsonp callback.
|
||||||
|
--
|
||||||
|
-- Basically, if the user requested a \"text\/javascript\" and supplied a
|
||||||
|
-- \"callback\" GET parameter, ask the application for an
|
||||||
|
-- \"application/json\" response, then convern that into a JSONP response,
|
||||||
|
-- having a content type of \"text\/javascript\" and calling the specified
|
||||||
|
-- callback function.
|
||||||
|
jsonp :: Middleware
|
||||||
|
jsonp app env = do
|
||||||
|
let accept = fromMaybe "" $ lookup "Accept" $ http env
|
||||||
|
let gets = decodeUrlPairs $ queryString env
|
||||||
|
let callback :: Maybe String
|
||||||
|
callback =
|
||||||
|
if "text/javascript" `isInfixOf` accept
|
||||||
|
then lookup "callback" gets
|
||||||
|
else Nothing
|
||||||
|
let env' =
|
||||||
|
case callback of
|
||||||
|
Nothing -> env
|
||||||
|
Just _ -> env
|
||||||
|
{ http = changeVal "Accept"
|
||||||
|
"application/json"
|
||||||
|
$ http env
|
||||||
|
}
|
||||||
|
res <- app env'
|
||||||
|
let ctype = fromMaybe "" $ lookup "Content-Type" $ headers res
|
||||||
|
case callback of
|
||||||
|
Nothing -> return res
|
||||||
|
Just c ->
|
||||||
|
case ctype of
|
||||||
|
"application/json" -> return $ res
|
||||||
|
{ headers = changeVal "Content-Type"
|
||||||
|
"text/javascript"
|
||||||
|
$ headers res
|
||||||
|
, body = B8.concat
|
||||||
|
[ B8.pack c -- NOTE uses Latin-1 encoding.
|
||||||
|
, B8.singleton '('
|
||||||
|
, body res
|
||||||
|
, B8.singleton ')'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ -> return res
|
||||||
|
|
||||||
|
changeVal :: String -> String -> [(String, String)] -> [(String, String)]
|
||||||
|
changeVal key val old = (key, val) : filter (\(k, _) -> k /= key) old
|
||||||
@ -43,6 +43,9 @@ import Web.Restful.Definitions
|
|||||||
import Web.Restful.Constants
|
import Web.Restful.Constants
|
||||||
import Web.Restful.Resource
|
import Web.Restful.Resource
|
||||||
|
|
||||||
|
import Data.Convertible
|
||||||
|
import Control.Arrow ((***))
|
||||||
|
|
||||||
-- | A data type that can be turned into a Hack application.
|
-- | A data type that can be turned into a Hack application.
|
||||||
class ResourceName a => RestfulApp a where
|
class ResourceName a => RestfulApp a where
|
||||||
-- | The encryption key to be used for encrypting client sessions.
|
-- | The encryption key to be used for encrypting client sessions.
|
||||||
@ -160,9 +163,15 @@ envToRawRequest urlParams' env =
|
|||||||
gets' = decodeUrlPairs $ Hack.queryString env :: [(String, String)]
|
gets' = decodeUrlPairs $ Hack.queryString env :: [(String, String)]
|
||||||
clength = tryLookup "0" "Content-Length" $ Hack.http env
|
clength = tryLookup "0" "Content-Length" $ Hack.http env
|
||||||
ctype = tryLookup "" "Content-Type" $ Hack.http env
|
ctype = tryLookup "" "Content-Type" $ Hack.http env
|
||||||
(posts, files) = parsePost ctype clength
|
(posts, files) = map (convertSuccess *** convertSuccess) ***
|
||||||
|
map (convertSuccess *** convertFileInfo)
|
||||||
|
$ parsePost ctype clength
|
||||||
$ Hack.hackInput env
|
$ Hack.hackInput env
|
||||||
rawCookie = tryLookup "" "Cookie" $ Hack.http env
|
rawCookie = tryLookup "" "Cookie" $ Hack.http env
|
||||||
cookies' = decodeCookies rawCookie :: [(String, String)]
|
cookies' = decodeCookies rawCookie :: [(String, String)]
|
||||||
langs = ["en"] -- FIXME
|
langs = ["en"] -- FIXME
|
||||||
in RawRequest rawPieces urlParams' gets' posts cookies' files env langs
|
in RawRequest rawPieces urlParams' gets' posts cookies' files env langs
|
||||||
|
|
||||||
|
convertFileInfo :: ConvertSuccess a b => FileInfo a c -> FileInfo b c
|
||||||
|
convertFileInfo (FileInfo a b c) =
|
||||||
|
FileInfo (convertSuccess a) (convertSuccess b) c
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
--
|
--
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
|
|
||||||
module Web.Restful.Response.AtomFeed
|
module Web.Restful.Helpers.AtomFeed
|
||||||
( AtomFeed (..)
|
( AtomFeed (..)
|
||||||
, AtomFeedEntry (..)
|
, AtomFeedEntry (..)
|
||||||
) where
|
) where
|
||||||
@ -14,7 +14,7 @@
|
|||||||
--
|
--
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
|
|
||||||
module Web.Restful.Response.Sitemap
|
module Web.Restful.Helpers.Sitemap
|
||||||
( sitemap
|
( sitemap
|
||||||
, robots
|
, robots
|
||||||
, SitemapUrl (..)
|
, SitemapUrl (..)
|
||||||
@ -56,6 +56,7 @@ import Web.Encodings
|
|||||||
import Data.Time.Calendar (Day, fromGregorian)
|
import Data.Time.Calendar (Day, fromGregorian)
|
||||||
import Data.Char (isDigit)
|
import Data.Char (isDigit)
|
||||||
import Data.Object.Translate (Language)
|
import Data.Object.Translate (Language)
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
|
||||||
-- $param_overview
|
-- $param_overview
|
||||||
-- In Restful, all of the underlying parameter values are strings. They can
|
-- In Restful, all of the underlying parameter values are strings. They can
|
||||||
@ -231,7 +232,7 @@ data RawRequest = RawRequest
|
|||||||
, rawGetParams :: [(ParamName, ParamValue)]
|
, rawGetParams :: [(ParamName, ParamValue)]
|
||||||
, rawPostParams :: [(ParamName, ParamValue)]
|
, rawPostParams :: [(ParamName, ParamValue)]
|
||||||
, rawCookies :: [(ParamName, ParamValue)]
|
, rawCookies :: [(ParamName, ParamValue)]
|
||||||
, rawFiles :: [(ParamName, FileInfo)]
|
, rawFiles :: [(ParamName, FileInfo String BL.ByteString)]
|
||||||
, rawEnv :: Hack.Env
|
, rawEnv :: Hack.Env
|
||||||
, rawLanguages :: [Language]
|
, rawLanguages :: [Language]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: restful
|
name: restful
|
||||||
version: 0.1.11
|
version: 0.1.12
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -21,32 +21,27 @@ library
|
|||||||
else
|
else
|
||||||
Buildable: True
|
Buildable: True
|
||||||
build-depends: base >= 4 && < 5,
|
build-depends: base >= 4 && < 5,
|
||||||
old-locale >= 1.0.0.1,
|
old-locale >= 1.0.0.1 && < 1.1,
|
||||||
time >= 1.1.3,
|
time >= 1.1.3 && < 1.2,
|
||||||
hack-middleware-clientsession,
|
|
||||||
hack-middleware-jsonp >= 0.0.2,
|
|
||||||
hack-middleware-cleanpath >= 0.0.1,
|
|
||||||
hack-middleware-gzip,
|
|
||||||
hack-handler-cgi >= 0.0.2,
|
|
||||||
hack >= 2009.5.19,
|
hack >= 2009.5.19,
|
||||||
split >= 0.1.1,
|
split >= 0.1.1 && < 0.2,
|
||||||
authenticate >= 0.2.1,
|
authenticate >= 0.2.1 && < 0.3,
|
||||||
data-default >= 0.2,
|
data-default >= 0.2 && < 0.3,
|
||||||
predicates >= 0.1,
|
predicates >= 0.1 && < 0.2,
|
||||||
bytestring >= 0.9.1.4,
|
bytestring >= 0.9.1.4 && < 0.10,
|
||||||
bytestring-class,
|
web-encodings >= 0.2.0 && < 0.3,
|
||||||
web-encodings >= 0.0.1,
|
data-object >= 0.2.0 && < 0.3,
|
||||||
data-object >= 0.2.0,
|
yaml >= 0.2.0 && < 0.3,
|
||||||
data-object-translate,
|
enumerable >= 0.0.3 && < 0.1,
|
||||||
yaml >= 0.2.0,
|
directory >= 1 && < 1.1,
|
||||||
enumerable >= 0.0.3,
|
transformers >= 0.1.4.0 && < 0.2,
|
||||||
directory >= 1,
|
monads-fd >= 0.0.0.1 && < 0.1,
|
||||||
transformers >= 0.1.4.0,
|
attempt >= 0.0.2 && < 0.1,
|
||||||
monads-fd >= 0.0.0.1,
|
|
||||||
attempt >= 0.0.2,
|
|
||||||
syb,
|
syb,
|
||||||
text >= 0.5,
|
text >= 0.5 && < 0.6,
|
||||||
convertible >= 1.2.0
|
convertible >= 1.2.0 && < 1.3,
|
||||||
|
clientsession >= 0.0.1 && < 0.1,
|
||||||
|
zlib >= 0.5.2.0 && < 0.6
|
||||||
exposed-modules: Web.Restful,
|
exposed-modules: Web.Restful,
|
||||||
Web.Restful.Constants,
|
Web.Restful.Constants,
|
||||||
Web.Restful.Request,
|
Web.Restful.Request,
|
||||||
@ -57,11 +52,16 @@ library
|
|||||||
Web.Restful.Application,
|
Web.Restful.Application,
|
||||||
Web.Restful.Resource,
|
Web.Restful.Resource,
|
||||||
Data.Object.Instances,
|
Data.Object.Instances,
|
||||||
|
Data.Object.Translate,
|
||||||
Hack.Middleware.MethodOverride,
|
Hack.Middleware.MethodOverride,
|
||||||
|
Hack.Middleware.ClientSession,
|
||||||
|
Hack.Middleware.Jsonp,
|
||||||
|
Hack.Middleware.CleanPath,
|
||||||
|
Hack.Middleware.Gzip,
|
||||||
Web.Restful.Helpers.Auth,
|
Web.Restful.Helpers.Auth,
|
||||||
Web.Restful.Helpers.Static,
|
Web.Restful.Helpers.Static,
|
||||||
Web.Restful.Response.AtomFeed,
|
Web.Restful.Helpers.AtomFeed,
|
||||||
Web.Restful.Response.Sitemap
|
Web.Restful.Helpers.Sitemap
|
||||||
ghc-options: -Wall -Werror
|
ghc-options: -Wall -Werror
|
||||||
|
|
||||||
executable runtests
|
executable runtests
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user