diff --git a/static/docs/0.1.1/heist/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.1.1/heist/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index f794d47..0000000 --- a/static/docs/0.1.1/heist/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,225 +0,0 @@ - - -
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the apply splice. - | |||||
| |||||
Default attribute name for the apply tag. - | |||||
| |||||
Implementation of the apply splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the bind splice. - | |||||
| |||||
Default attribute name for the bind tag. - | |||||
| |||||
Implementation of the bind splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the ignore splice. - | |||||
| |||||
The ignore tag and everything it surrounds disappears in the - rendered output. - | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Default name for the markdown splice. - | |||||||||||||
| |||||||||||||
Implementation of the markdown splice. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
Modifies a TemplateState to include a static tag. - | |||||
| |||||
Clears the static tag state. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Documentation | |||||
module Text.Templating.Heist.Splices.Apply | |||||
module Text.Templating.Heist.Splices.Bind | |||||
module Text.Templating.Heist.Splices.Ignore | |||||
module Text.Templating.Heist.Splices.Markdown | |||||
module Text.Templating.Heist.Splices.Static | |||||
Produced by Haddock version 2.6.1 |
| ||||||||||||
| ||||||||||||
| ||||||||||||
Description | ||||||||||||
This module contains the core definitions for the Heist template system. - The Heist template system is based on XML/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - The most important concept in Heist is the Splice. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. Splice is implemented as a type synonym type Splice m = - TemplateMonad m [Node], and TemplateMonad has a function getParamNode - that lets you get the input node. - Suppose you have a place on your page where you want to display a link with - the text "Logout username" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - getUser :: MyAppMonad (Maybe ByteString) that gets the current user. - You can implement this functionality with a Splice as follows: - - import Text.XML.Expat.Tree - - link :: ByteString -> ByteString -> Node - link target text = X.Element "a" [("href", target)] [X.Text text] - - loginLink :: Node - loginLink = link "/login" "Login" - - logoutLink :: ByteString -> Node - logoutLink user = link "/logout" (B.append "Logout " user) - - loginLogoutSplice :: Splice MyAppMonad - loginLogoutSplice = do - user <- lift getUser - return $ [maybe loginLink logoutLink user] - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the TemplateState data structure. The - following code demonstrates how this splice would be used. - mySplices = [ ("loginLogout", loginLogoutSplice) ] - - main = do - ets <- loadTemplates "templates" $ - foldr (uncurry bindSplice) emptyTemplateState mySplices - let ts = either error id ets - t <- runMyAppMonad $ renderTemplate ts "index" - print $ maybe "Page not found" id t - Here we build up our TemplateState by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final TemplateState wrapped in an Either to handle - errors. Then we use this TemplateState to render our templates. - | ||||||||||||
Synopsis | ||||||||||||
Types - | ||||||||||||
| ||||||||||||
Heist templates are XML documents. The hexpat library is polymorphic over - the type of strings, so here we define a Node alias to fix the string - types of the tag names and tag bodies to ByteString. - | ||||||||||||
| ||||||||||||
A Splice is a TemplateMonad computation that returns [Node]. - | ||||||||||||
| ||||||||||||
A Template is a forest of XML nodes. - | ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
Functions and declarations on TemplateState values - | ||||||||||||
| ||||||||||||
Adds a template to the template state. - | ||||||||||||
| ||||||||||||
An empty template state, with Heist's default splices (<bind> and - <apply>) mapped. - | ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
Convenience function for looking up a splice. - | ||||||||||||
| ||||||||||||
Sets the templateMap in a TemplateState. - | ||||||||||||
| ||||||||||||
Traverses the specified directory structure and builds a - TemplateState by loading all the files with a .tpl extension. - | ||||||||||||
Hook functions - | ||||||||||||
Heist hooks allow you to modify templates when they are loaded and before - and after they are run. Every time you call one of the addAbcHook - functions the hook is added to onto the processing pipeline. The hooks - processes the template in the order that they were added to the - TemplateState. - The pre-run and post-run hooks are run before and after every template is - run/rendered. You should be careful what code you put in these hooks - because it can significantly affect the performance of your site. - | ||||||||||||
| ||||||||||||
Adds an on-load hook to a TemplateState. - | ||||||||||||
| ||||||||||||
Adds a pre-run hook to a TemplateState. - | ||||||||||||
| ||||||||||||
Adds a post-run hook to a TemplateState. - | ||||||||||||
TemplateMonad functions - | ||||||||||||
| ||||||||||||
Stops the recursive processing of splices. - | ||||||||||||
| ||||||||||||
Gets the node currently being processed. - | ||||||||||||
| ||||||||||||
Performs splice processing on a list of nodes. - | ||||||||||||
| ||||||||||||
Gets the current context - | ||||||||||||
Functions for running splices and templates - | ||||||||||||
| ||||||||||||
Looks up a template name in the supplied TemplateState and runs - it in the underlying monad. - | ||||||||||||
| ||||||||||||
Looks up a template name evaluates it. Same as runTemplate except it - runs in TemplateMonad instead of m. - | ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
Renders a template from the specified TemplateState. - | ||||||||||||
| ||||||||||||
Binds a list of constant string splices - | ||||||||||||
Misc functions - | ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
Runs a template in the underlying monad. Similar to runSplice - except that templates don't require a Node as a parameter. - | ||||||||||||
| ||||||||||||
Reads an XML document from disk. - | ||||||||||||
| ||||||||||||
Modifies a TemplateState to include a static tag. - | ||||||||||||
| ||||||||||||
| ||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||
heist-0.1.1: An xhtml templating system | |||||||||||||||||||||||||||||||||||||||
An xhtml templating system - | |||||||||||||||||||||||||||||||||||||||
Modules | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE OverloadedStrings #-} -module Text.Templating.Heist.Constants where - -import Data.ByteString.Char8 (ByteString) -import qualified Data.Map as Map -import Data.Map (Map) - -htmlEntityLookupTable :: Map ByteString ByteString -htmlEntityLookupTable = Map.fromList [ - ("acute" , "\xc2\xb4") - , ("cedil" , "\xc2\xb8") - , ("circ" , "\xcb\x86") - , ("macr" , "\xc2\xaf") - , ("middot" , "\xc2\xb7") - , ("tilde" , "\xcb\x9c") - , ("uml" , "\xc2\xa8") - , ("Aacute" , "\xc3\x81") - , ("aacute" , "\xc3\xa1") - , ("Acirc" , "\xc3\x82") - , ("acirc" , "\xc3\xa2") - , ("AElig" , "\xc3\x86") - , ("aelig" , "\xc3\xa6") - , ("Agrave" , "\xc3\x80") - , ("agrave" , "\xc3\xa0") - , ("Aring" , "\xc3\x85") - , ("aring" , "\xc3\xa5") - , ("Atilde" , "\xc3\x83") - , ("atilde" , "\xc3\xa3") - , ("Auml" , "\xc3\x84") - , ("auml" , "\xc3\xa4") - , ("Ccedil" , "\xc3\x87") - , ("ccedil" , "\xc3\xa7") - , ("Eacute" , "\xc3\x89") - , ("eacute" , "\xc3\xa9") - , ("Ecirc" , "\xc3\x8a") - , ("ecirc" , "\xc3\xaa") - , ("Egrave" , "\xc3\x88") - , ("egrave" , "\xc3\xa8") - , ("ETH" , "\xc3\x90") - , ("eth" , "\xc3\xb0") - , ("Euml" , "\xc3\x8b") - , ("euml" , "\xc3\xab") - , ("Iacute" , "\xc3\x8d") - , ("iacute" , "\xc3\xad") - , ("Icirc" , "\xc3\x8e") - , ("icirc" , "\xc3\xae") - , ("Igrave" , "\xc3\x8c") - , ("igrave" , "\xc3\xac") - , ("Iuml" , "\xc3\x8f") - , ("iuml" , "\xc3\xaf") - , ("Ntilde" , "\xc3\x91") - , ("ntilde" , "\xc3\xb1") - , ("Oacute" , "\xc3\x93") - , ("oacute" , "\xc3\xb3") - , ("Ocirc" , "\xc3\x94") - , ("ocirc" , "\xc3\xb4") - , ("OElig" , "\xc5\x92") - , ("oelig" , "\xc5\x93") - , ("Ograve" , "\xc3\x92") - , ("ograve" , "\xc3\xb2") - , ("Oslash" , "\xc3\x98") - , ("oslash" , "\xc3\xb8") - , ("Otilde" , "\xc3\x95") - , ("otilde" , "\xc3\xb5") - , ("Ouml" , "\xc3\x96") - , ("ouml" , "\xc3\xb6") - , ("Scaron" , "\xc5\xa0") - , ("scaron" , "\xc5\xa1") - , ("szlig" , "\xc3\x9f") - , ("THORN" , "\xc3\x9e") - , ("thorn" , "\xc3\xbe") - , ("Uacute" , "\xc3\x9a") - , ("uacute" , "\xc3\xba") - , ("Ucirc" , "\xc3\x9b") - , ("ucirc" , "\xc3\xbb") - , ("Ugrave" , "\xc3\x99") - , ("ugrave" , "\xc3\xb9") - , ("Uuml" , "\xc3\x9c") - , ("uuml" , "\xc3\xbc") - , ("Yacute" , "\xc3\x9d") - , ("yacute" , "\xc3\xbd") - , ("yuml" , "\xc3\xbf") - , ("Yuml" , "\xc5\xb8") - , ("cent" , "\xc2\xa2") - , ("curren" , "\xc2\xa4") - , ("euro" , "\xe2\x82\xac") - , ("pound" , "\xc2\xa3") - , ("yen" , "\xc2\xa5") - , ("brvbar" , "\xc2\xa6") - , ("bull" , "\xe2\x80\xa2") - , ("copy" , "\xc2\xa9") - , ("dagger" , "\xe2\x80\xa0") - , ("Dagger" , "\xe2\x80\xa1") - , ("frasl" , "\xe2\x81\x84") - , ("hellip" , "\xe2\x80\xa6") - , ("iexcl" , "\xc2\xa1") - , ("image" , "\xe2\x84\x91") - , ("iquest" , "\xc2\xbf") - , ("mdash" , "\xe2\x80\x94") - , ("ndash" , "\xe2\x80\x93") - , ("not" , "\xc2\xac") - , ("oline" , "\xe2\x80\xbe") - , ("ordf" , "\xc2\xaa") - , ("ordm" , "\xc2\xba") - , ("para" , "\xc2\xb6") - , ("permil" , "\xe2\x80\xb0") - , ("prime" , "\xe2\x80\xb2") - , ("Prime" , "\xe2\x80\xb3") - , ("real" , "\xe2\x84\x9c") - , ("reg" , "\xc2\xae") - , ("sect" , "\xc2\xa7") - , ("shy" , "\173") - , ("sup1" , "\xc2\xb9") - , ("trade" , "\xe2\x84\xa2") - , ("weierp" , "\xe2\x84\x98") - , ("bdquo" , "\xe2\x80\x9e") - , ("laquo" , "\xc2\xab") - , ("ldquo" , "\xe2\x80\x9c") - , ("lsaquo" , "\xe2\x80\xb9") - , ("lsquo" , "\xe2\x80\x98") - , ("raquo" , "\xc2\xbb") - , ("rdquo" , "\xe2\x80\x9d") - , ("rsaquo" , "\xe2\x80\xba") - , ("rsquo" , "\xe2\x80\x99") - , ("sbquo" , "\xe2\x80\x9a") - , ("emsp" , "\xe2\x80\x83") - , ("ensp" , "\xe2\x80\x82") - , ("nbsp" , "\x20") - , ("thinsp" , "\xe2\x80\x89") - , ("zwj" , "\xe2\x80\x8d") - , ("zwnj" , "\xe2\x80\x8c") - , ("deg" , "\xc2\xb0") - , ("divide" , "\xc3\xb7") - , ("frac12" , "\xc2\xbd") - , ("frac14" , "\xc2\xbc") - , ("frac34" , "\xc2\xbe") - , ("ge" , "\xe2\x89\xa5") - , ("le" , "\xe2\x89\xa4") - , ("minus" , "\xe2\x88\x92") - , ("sup2" , "\xc2\xb2") - , ("sup3" , "\xc2\xb3") - , ("times" , "\xc3\x97") - , ("alefsym" , "\xe2\x84\xb5") - , ("and" , "\xe2\x88\xa7") - , ("ang" , "\xe2\x88\xa0") - , ("asymp" , "\xe2\x89\x88") - , ("cap" , "\xe2\x88\xa9") - , ("cong" , "\xe2\x89\x85") - , ("cup" , "\xe2\x88\xaa") - , ("empty" , "\xe2\x88\x85") - , ("equiv" , "\xe2\x89\xa1") - , ("exist" , "\xe2\x88\x83") - , ("fnof" , "\xc6\x92") - , ("forall" , "\xe2\x88\x80") - , ("infin" , "\xe2\x88\x9e") - , ("int" , "\xe2\x88\xab") - , ("isin" , "\xe2\x88\x88") - , ("lang" , "\xe3\x80\x88") - , ("lceil" , "\xe2\x8c\x88") - , ("lfloor" , "\xe2\x8c\x8a") - , ("lowast" , "\xe2\x88\x97") - , ("micro" , "\xc2\xb5") - , ("nabla" , "\xe2\x88\x87") - , ("ne" , "\xe2\x89\xa0") - , ("ni" , "\xe2\x88\x8b") - , ("notin" , "\xe2\x88\x89") - , ("nsub" , "\xe2\x8a\x84") - , ("oplus" , "\xe2\x8a\x95") - , ("or" , "\xe2\x88\xa8") - , ("otimes" , "\xe2\x8a\x97") - , ("part" , "\xe2\x88\x82") - , ("perp" , "\xe2\x8a\xa5") - , ("plusmn" , "\xc2\xb1") - , ("prod" , "\xe2\x88\x8f") - , ("prop" , "\xe2\x88\x9d") - , ("radic" , "\xe2\x88\x9a") - , ("rang" , "\xe3\x80\x89") - , ("rceil" , "\xe2\x8c\x89") - , ("rfloor" , "\xe2\x8c\x8b") - , ("sdot" , "\xe2\x8b\x85") - , ("sim" , "\xe2\x88\xbc") - , ("sub" , "\xe2\x8a\x82") - , ("sube" , "\xe2\x8a\x86") - , ("sum" , "\xe2\x88\x91") - , ("sup" , "\xe2\x8a\x83") - , ("supe" , "\xe2\x8a\x87") - , ("there4" , "\xe2\x88\xb4") - , ("Alpha" , "\xce\x91") - , ("alpha" , "\xce\xb1") - , ("Beta" , "\xce\x92") - , ("beta" , "\xce\xb2") - , ("Chi" , "\xce\xa7") - , ("chi" , "\xcf\x87") - , ("Delta" , "\xce\x94") - , ("delta" , "\xce\xb4") - , ("Epsilon" , "\xce\x95") - , ("epsilon" , "\xce\xb5") - , ("Eta" , "\xce\x97") - , ("eta" , "\xce\xb7") - , ("Gamma" , "\xce\x93") - , ("gamma" , "\xce\xb3") - , ("Iota" , "\xce\x99") - , ("iota" , "\xce\xb9") - , ("Kappa" , "\xce\x9a") - , ("kappa" , "\xce\xba") - , ("Lambda" , "\xce\x9b") - , ("lambda" , "\xce\xbb") - , ("Mu" , "\xce\x9c") - , ("mu" , "\xce\xbc") - , ("Nu" , "\xce\x9d") - , ("nu" , "\xce\xbd") - , ("Omega" , "\xce\xa9") - , ("omega" , "\xcf\x89") - , ("Omicron" , "\xce\x9f") - , ("omicron" , "\xce\xbf") - , ("Phi" , "\xce\xa6") - , ("phi" , "\xcf\x86") - , ("Pi" , "\xce\xa0") - , ("pi" , "\xcf\x80") - , ("piv" , "\xcf\x96") - , ("Psi" , "\xce\xa8") - , ("psi" , "\xcf\x88") - , ("Rho" , "\xce\xa1") - , ("rho" , "\xcf\x81") - , ("Sigma" , "\xce\xa3") - , ("sigma" , "\xcf\x83") - , ("sigmaf" , "\xcf\x82") - , ("Tau" , "\xce\xa4") - , ("tau" , "\xcf\x84") - , ("Theta" , "\xce\x98") - , ("theta" , "\xce\xb8") - , ("thetasym" , "\xcf\x91") - , ("upsih" , "\xcf\x92") - , ("Upsilon" , "\xce\xa5") - , ("upsilon" , "\xcf\x85") - , ("Xi" , "\xce\x9e") - , ("xi" , "\xce\xbe") - , ("Zeta" , "\xce\x96") - , ("zeta" , "\xce\xb6") - , ("crarr" , "\xe2\x86\xb5") - , ("darr" , "\xe2\x86\x93") - , ("dArr" , "\xe2\x87\x93") - , ("harr" , "\xe2\x86\x94") - , ("hArr" , "\xe2\x87\x94") - , ("larr" , "\xe2\x86\x90") - , ("lArr" , "\xe2\x87\x90") - , ("rarr" , "\xe2\x86\x92") - , ("rArr" , "\xe2\x87\x92") - , ("uarr" , "\xe2\x86\x91") - , ("uArr" , "\xe2\x87\x91") - , ("clubs" , "\xe2\x99\xa3") - , ("diams" , "\xe2\x99\xa6") - , ("hearts" , "\xe2\x99\xa5") - , ("spades" , "\xe2\x99\xa0") - , ("loz" , "\xe2\x97\x8a") ] -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Internal.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Internal.html deleted file mode 100644 index fa22e13..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Internal.html +++ /dev/null @@ -1,503 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Text.Templating.Heist.Internal where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad.CatchIO -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as L -import Data.Either -import qualified Data.Foldable as F -import Data.List -import qualified Data.Map as Map -import Data.Map (Map) -import Prelude hiding (catch) -import System.Directory.Tree hiding (name) -import Text.XML.Expat.Format -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Constants - ------------------------------------------------------------------------------- --- Types ------------------------------------------------------------------------------- - --- | Heist templates are XML documents. The hexpat library is polymorphic over --- the type of strings, so here we define a 'Node' alias to fix the string --- types of the tag names and tag bodies to 'ByteString'. -type Node = X.Node ByteString ByteString - - ------------------------------------------------------------------------------- --- | A 'Template' is a forest of XML nodes. -type Template = [Node] - - ------------------------------------------------------------------------------- --- | Reversed list of directories -type TPath = [ByteString] - - ------------------------------------------------------------------------------- -type TemplateMap = Map TPath Template - - ------------------------------------------------------------------------------- --- | Holds all the state information needed for template processing: --- --- * a collection of named templates. If you use the @\<apply --- template=\"foo\"\>@ tag to include another template by name, @\"foo\"@ --- is looked up in here. --- --- * the mapping from tag names to 'Splice's. --- --- * a flag to control whether we will recurse during splice processing. --- --- We'll illustrate the recursion flag with a small example template: --- --- > <foo> --- > <bar> --- > ... --- > </bar> --- > </foo> --- --- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ --- splice will result in a list of nodes @L@; if the recursion flag is on we --- will recursively scan @L@ for splices, otherwise @L@ will be included in the --- output verbatim. -data TemplateState m = TemplateState { - -- | A mapping of splice names to splice actions - _spliceMap :: SpliceMap m - -- | A mapping of template names to templates - , _templateMap :: TemplateMap - -- | A flag to control splice recursion - , _recurse :: Bool - , _curContext :: TPath - , _recursionDepth :: Int - , _onLoadHook :: Template -> IO Template - , _preRunHook :: Template -> m Template - , _postRunHook :: Template -> m Template -} - - ------------------------------------------------------------------------------- -instance Eq (TemplateState m) where - a == b = (_recurse a == _recurse b) && - (_templateMap a == _templateMap b) && - (_curContext a == _curContext b) - - ------------------------------------------------------------------------------- --- | 'TemplateMonad' is a monad transformer that gives you access to the 'Node' --- being processed (using the 'MonadReader' instance) as well as holding the --- 'TemplateState' that contains splice and template mappings (accessible --- using the 'MonadState' instance. -newtype TemplateMonad m a = TemplateMonad (RWST Node () (TemplateState m) m a) - deriving ( Monad - , MonadIO - , MonadCatchIO - , MonadReader Node - , MonadState (TemplateState m) ) - - ------------------------------------------------------------------------------- -instance (Monad m) => Monoid (TemplateState m) where - mempty = TemplateState Map.empty Map.empty True [] 0 - return return return - - (TemplateState s1 t1 r1 _ d1 o1 b1 a1) `mappend` - (TemplateState s2 t2 r2 c2 d2 o2 b2 a2) = - TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) - where - s = s1 `mappend` s2 - t = t1 `mappend` t2 - r = r1 && r2 - d = max d1 d2 - - ------------------------------------------------------------------------------- -instance MonadTrans TemplateMonad where - lift = TemplateMonad . lift - ------------------------------------------------------------------------------- --- | A Splice is a TemplateMonad computation that returns [Node]. -type Splice m = TemplateMonad m Template - - ------------------------------------------------------------------------------- --- | SpliceMap associates a name and a Splice. -type SpliceMap m = Map ByteString (Splice m) - - ------------------------------------------------------------------------------- --- TemplateState functions ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- --- | Adds an on-load hook to a `TemplateState`. -addOnLoadHook :: (Monad m) => - (Template -> IO Template) - -> TemplateState m - -> TemplateState m -addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a pre-run hook to a `TemplateState`. -addPreRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a post-run hook to a `TemplateState`. -addPostRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Bind a new splice declaration to a tag name within a 'TemplateState'. -bindSplice :: Monad m => - ByteString -- ^ tag name - -> Splice m -- ^ splice action - -> TemplateState m -- ^ source state - -> TemplateState m -bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a splice. -lookupSplice :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Splice m) -lookupSplice nm ts = Map.lookup nm $ _spliceMap ts - - ------------------------------------------------------------------------------- --- | Converts a path into an array of the elements in reverse order. -splitPaths :: ByteString -> TPath -splitPaths = reverse . B.split '/' - - ------------------------------------------------------------------------------- --- | Does a single template lookup without cascading up. -singleLookup :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm - - ------------------------------------------------------------------------------- --- | Searches for a template by looking in the full path then backing up into each --- of the parent directories until the template is found. -traversePath :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) -traversePath tm path name = - singleLookup tm path name `mplus` - traversePath tm (tail path) name - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a template. -lookupTemplate :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Template, TPath) -lookupTemplate nameStr ts = - f (_templateMap ts) path name - where (name:p) = case splitPaths nameStr of - [] -> [""] - ps -> ps - path = p ++ (_curContext ts) - f = if '/' `B.elem` nameStr - then singleLookup - else traversePath - - ------------------------------------------------------------------------------- --- | Sets the templateMap in a TemplateState. -setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m -setTemplates m ts = ts { _templateMap = m } - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -insertTemplate :: Monad m => - TPath - -> Template - -> TemplateState m - -> TemplateState m -insertTemplate p t st = - setTemplates (Map.insert p t (_templateMap st)) st - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -addTemplate :: Monad m => - ByteString - -> Template - -> TemplateState m - -> TemplateState m -addTemplate n t st = insertTemplate (splitPaths n) t st - - ------------------------------------------------------------------------------- --- | Gets the node currently being processed. -getParamNode :: Monad m => TemplateMonad m Node -getParamNode = ask - - ------------------------------------------------------------------------------- --- | Stops the recursive processing of splices. -stopRecursion :: Monad m => TemplateMonad m () -stopRecursion = modify (\st -> st { _recurse = False }) - - ------------------------------------------------------------------------------- --- | Sets the current context -setContext :: Monad m => TPath -> TemplateMonad m () -setContext c = modify (\st -> st { _curContext = c }) - - ------------------------------------------------------------------------------- --- | Gets the current context -getContext :: Monad m => TemplateMonad m TPath -getContext = gets _curContext - - ------------------------------------------------------------------------------- --- | Performs splice processing on a list of nodes. -runNodeList :: Monad m => [Node] -> Splice m -runNodeList nodes = liftM concat $ sequence (map runNode nodes) - - ------------------------------------------------------------------------------- --- | Performs splice processing on a single node. -runNode :: Monad m => Node -> Splice m -runNode n@(X.Text _) = return [n] -runNode n@(X.Element nm _ ch) = do - s <- liftM (lookupSplice nm) get - maybe runChildren (recurseSplice n) s - - where - runChildren = do - newKids <- runNodeList ch - return [X.modifyChildren (const newKids) n] - - ------------------------------------------------------------------------------- --- | The maximum recursion depth. (Used to prevent infinite loops.) -mAX_RECURSION_DEPTH :: Int -mAX_RECURSION_DEPTH = 20 - - ------------------------------------------------------------------------------- --- | Checks the recursion flag and recurses accordingly. Does not recurse --- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. -recurseSplice :: Monad m => Node -> Splice m -> Splice m -recurseSplice node splice = do - result <- local (const node) splice - ts' <- get - if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH - then do modify (\st -> st { _recursionDepth = _recursionDepth st + 1 }) - res <- runNodeList result - put ts' - return res - else return result - - ------------------------------------------------------------------------------- --- | Runs a splice in the underlying monad. Splices require two --- parameters, the template state, and an input node. -runSplice :: Monad m => - TemplateState m -- ^ The initial template state - -> Node -- ^ The splice's input node - -> Splice m -- ^ The splice - -> m [Node] -runSplice ts node (TemplateMonad splice) = do - (result,_,_) <- runRWST splice node ts - return result - - ------------------------------------------------------------------------------- --- | Runs a template in the underlying monad. Similar to runSplice --- except that templates don't require a Node as a parameter. -runRawTemplate :: Monad m => TemplateState m -> Template -> m [Node] -runRawTemplate ts template = - _preRunHook ts template >>= - runSplice ts (X.Text "") . runNodeList >>= - _postRunHook ts - - ------------------------------------------------------------------------------- --- | Looks up a template name in the supplied 'TemplateState' and runs --- it in the underlying monad. -runTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe [Node]) -runTemplate ts name = - maybe (return Nothing) - (\(t,ctx) -> - return . Just =<< - runRawTemplate (ts {_curContext = ctx}) t) - (lookupTemplate name ts) - - ------------------------------------------------------------------------------- --- | Looks up a template name evaluates it. Same as runTemplate except it --- runs in TemplateMonad instead of m. -evalTemplate :: Monad m - => ByteString - -> TemplateMonad m (Maybe [Node]) -evalTemplate name = do - ts <- get - lift $ runTemplate ts name - - ------------------------------------------------------------------------------- --- | Binds a list of constant string splices -bindStrings :: Monad m - => [(ByteString, ByteString)] - -> TemplateState m - -> TemplateState m -bindStrings pairs ts = foldr add ts pairs - where - add (n,v) = bindSplice n (return [X.Text v]) - - ------------------------------------------------------------------------------- --- | Renders a template with the specified parameters. This is the function --- to use when you want to "call" a template and pass in parameters from code. -callTemplate :: Monad m - => ByteString -- ^ The name of the template - -> [(ByteString, ByteString)] -- ^ Association list of - -- (name,value) parameter pairs - -> TemplateMonad m (Maybe Template) -callTemplate name params = do - modify $ bindStrings params - evalTemplate name - - ------------------------------------------------------------------------------- --- | Renders a template from the specified TemplateState. -renderTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe ByteString) -renderTemplate ts name = do - ns <- runTemplate ts name - return $ (Just . formatList') =<< ns - - ------------------------------------------------------------------------------- -heistExpatOptions :: X.ParserOptions ByteString ByteString -heistExpatOptions = - X.defaultParserOptions { - X.parserEncoding = Just X.UTF8 - , X.entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) - } - ------------------------------------------------------------------------------- --- Template loading ------------------------------------------------------------------------------- - --- | Reads an XML document from disk. -getDoc :: String -> IO (Either String Template) -getDoc f = do - bs <- catch (liftM Right $ B.readFile f) - (\(e::SomeException) -> return $ Left $ show e) - let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" - return $ (mapRight X.getChildren . - mapLeft genErrorMsg . - X.parse' heistExpatOptions . wrap) =<< bs - where - genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str - locMsg (X.XMLParseLocation line col _ _) = - "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" - translate "junk after document element" = "document must have a single root element" - translate s = s - ------------------------------------------------------------------------------- -mapLeft :: (a -> b) -> Either a c -> Either b c -mapLeft g = either (Left . g) Right -mapRight :: (b -> c) -> Either a b -> Either a c -mapRight g = either Left (Right . g) - - ------------------------------------------------------------------------------- --- | Loads a template with the specified path and filename. The --- template is only loaded if it has a ".tpl" extension. -loadTemplate :: String -> String -> IO [Either String (TPath, Template)] --TemplateMap -loadTemplate path fname - | ".tpl" `isSuffixOf` fname = do - c <- getDoc fname - return [fmap (\t -> (splitPaths $ B.pack tName, t)) c] - | otherwise = return [] - where tName = drop ((length path)+1) $ - take ((length fname) - 4) fname - - ------------------------------------------------------------------------------- --- | Traverses the specified directory structure and builds a --- TemplateState by loading all the files with a ".tpl" extension. -loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) -loadTemplates dir ts = do - d <- readDirectoryWith (loadTemplate dir) dir - let tlist = F.fold (free d) - errs = lefts tlist - case errs of - [] -> liftM Right $ foldM loadHook ts $ rights tlist - _ -> return $ Left $ unlines errs - - ------------------------------------------------------------------------------- --- | Runs the onLoad hook on the template and returns the `TemplateState` --- with the result inserted. -loadHook :: Monad m => TemplateState m -> (TPath, Template) -> IO (TemplateState m) -loadHook ts (tp, t) = do - t' <- _onLoadHook ts t - return $ insertTemplate tp t' ts - - ------------------------------------------------------------------------------- --- These are here until we can get them into hexpat. ------------------------------------------------------------------------------- - -formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> L.ByteString -formatList nodes = foldl L.append L.empty $ map formatNode nodes - -formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> B.ByteString -formatList' = B.concat . L.toChunks . formatList - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index 83efe53..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Apply where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - ------------------------------------------------------------------------------- --- | Default name for the apply splice. -applyTag :: ByteString -applyTag = "apply" - - ------------------------------------------------------------------------------- --- | Default attribute name for the apply tag. -applyAttr :: ByteString -applyAttr = "template" - - ------------------------------------------------------------------------------- --- | Implementation of the apply splice. -applyImpl :: Monad m => Splice m -applyImpl = do - node <- getParamNode - case X.getAttribute node applyAttr of - Nothing -> return [] -- TODO: error handling - Just attr -> do - st <- get - processedChildren <- runNodeList $ X.getChildren node - modify (bindSplice "content" $ return processedChildren) - maybe (return []) -- TODO: error handling - (\(t,ctx) -> do setContext ctx - result <- runNodeList t - put st - return result) - (lookupTemplate attr (st {_curContext = nextCtx attr st})) - where nextCtx name st - | B.isPrefixOf "/" name = [] - | otherwise = _curContext st - - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Bind.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Bind.html deleted file mode 100644 index 27c602f..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Bind.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Bind where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - --- | Default name for the bind splice. -bindTag :: ByteString -bindTag = "bind" - - ------------------------------------------------------------------------------- --- | Default attribute name for the bind tag. -bindAttr :: ByteString -bindAttr = "tag" - - ------------------------------------------------------------------------------- --- | Implementation of the bind splice. -bindImpl :: Monad m => Splice m -bindImpl = do - node <- getParamNode - maybe (return ()) - (add node) - (X.getAttribute node bindAttr) - return [] - - where - add node nm = modify $ bindSplice nm (return $ X.getChildren node) - - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Ignore.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Ignore.html deleted file mode 100644 index 94cfc79..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Ignore.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Ignore where - ------------------------------------------------------------------------------- -import Data.ByteString.Char8 (ByteString) - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | Default name for the ignore splice. -ignoreTag :: ByteString -ignoreTag = "ignore" - - ------------------------------------------------------------------------------- --- | The ignore tag and everything it surrounds disappears in the --- rendered output. -ignoreImpl :: Monad m => Splice m -ignoreImpl = return [] - - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Markdown.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Markdown.html deleted file mode 100644 index dcc4dce..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Markdown.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} - -module Text.Templating.Heist.Splices.Markdown where - -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy.Char8 as L -import Data.Maybe -import Control.Concurrent -import Control.Exception (evaluate, throwIO) -import Control.Monad -import Control.Monad.CatchIO -import Control.Monad.Trans -import Data.Typeable -import Prelude hiding (catch) -import System.Directory -import System.Exit -import System.IO -import System.Process -import Text.Templating.Heist.Internal -import Text.XML.Expat.Tree hiding (Node) - - -data PandocMissingException = PandocMissingException - deriving (Typeable) - -instance Show PandocMissingException where - show PandocMissingException = - "Cannot find the \"pandoc\" executable; is it on your $PATH?" - -instance Exception PandocMissingException - - -data MarkdownException = MarkdownException L.ByteString - deriving (Typeable) - -instance Show MarkdownException where - show (MarkdownException e) = - "Markdown error: pandoc replied:\n\n" ++ L.unpack e - -instance Exception MarkdownException - - ------------------------------------------------------------------------------- --- | Default name for the markdown splice. -markdownTag :: ByteString -markdownTag = "markdown" - ------------------------------------------------------------------------------- --- | Implementation of the markdown splice. -markdownSplice :: MonadIO m => Splice m -markdownSplice = do - pdMD <- liftIO $ findExecutable "pandoc" - - when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException - - tree <- getParamNode - markup <- liftIO $ - case getAttribute tree "file" of - Just f -> pandoc (fromJust pdMD) $ B.unpack f - Nothing -> pandocBS (fromJust pdMD) $ textContent tree - - let ee = parse' heistExpatOptions markup - case ee of - (Left e) -> throw $ MarkdownException - $ L.pack ("Error parsing markdown output: " ++ show e) - (Right n) -> return [n] - - -pandoc :: FilePath -> FilePath -> IO ByteString -pandoc pandocPath inputFile = do - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" - - when (isFail ex) $ throw $ MarkdownException serr - return $ B.concat $ L.toChunks - $ L.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - - -- FIXME: hardcoded path - args = [ "-S", "--no-wrap", "templates/"++inputFile ] - - -pandocBS :: FilePath -> ByteString -> IO ByteString -pandocBS pandocPath s = do - -- using the crummy string functions for convenience here - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s - - when (isFail ex) $ throw $ MarkdownException serr - return $ B.concat $ L.toChunks - $ L.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - args = [ "-S", "--no-wrap" ] - - --- a version of readProcessWithExitCode that does I/O properly -readProcessWithExitCode' - :: FilePath -- ^ command to run - -> [String] -- ^ any arguments - -> ByteString -- ^ standard input - -> IO (ExitCode,L.ByteString,L.ByteString) -- ^ exitcode, stdout, stderr -readProcessWithExitCode' cmd args input = do - (Just inh, Just outh, Just errh, pid) <- - createProcess (proc cmd args){ std_in = CreatePipe, - std_out = CreatePipe, - std_err = CreatePipe } - outMVar <- newEmptyMVar - - -- fork off a thread to start consuming stdout - out <- L.hGetContents outh - forkIO $ evaluate (L.length out) >> putMVar outMVar () - - -- fork off a thread to start consuming stderr - err <- L.hGetContents errh - forkIO $ evaluate (L.length err) >> putMVar outMVar () - - -- now write and flush any input - when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh - hClose inh -- done with stdin - - -- wait on the output - takeMVar outMVar - takeMVar outMVar - hClose outh - - -- wait on the process - ex <- waitForProcess pid - - return (ex, out, err) - - - - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Static.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Static.html deleted file mode 100644 index 734781d..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices-Static.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Static - ( StaticTagState - , bindStaticTag - , clearStaticTagCache - ) where - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Monad -import Control.Monad.Trans -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import Data.IORef -import qualified Data.Map as Map -import Data.Map (Map) -import Data.Maybe -import qualified Data.Set as Set -import System.Random -import Text.XML.Expat.Cursor -import Text.XML.Expat.Tree hiding (Node) - - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | State for storing static tag information -newtype StaticTagState = STS (MVar (Map ByteString Template)) - - ------------------------------------------------------------------------------- --- | Clears the static tag state. -clearStaticTagCache :: StaticTagState -> IO () -clearStaticTagCache (STS staticMVar) = - modifyMVar_ staticMVar (const $ return Map.empty) - - ------------------------------------------------------------------------------- --- | The "static" splice ensures that its contents are evaluated once and then --- cached. The cached contents are returned every time the splice is --- referenced. -staticImpl :: (MonadIO m) - => StaticTagState - -> TemplateMonad m Template -staticImpl (STS mv) = do - tree <- getParamNode - let i = fromJust $ getAttribute tree "id" - - mp <- liftIO $ readMVar mv - - (mp',ns) <- do - let mbn = Map.lookup i mp - case mbn of - Nothing -> do - nodes' <- runNodeList $ getChildren tree - return $! (Map.insert i nodes' mp, nodes') - (Just n) -> do - stopRecursion - return $! (mp,n) - - liftIO $ modifyMVar_ mv (const $ return mp') - - return ns - - ------------------------------------------------------------------------------- --- | Modifies a TemplateState to include a "static" tag. -bindStaticTag :: MonadIO m - => TemplateState m - -> IO (TemplateState m, StaticTagState) -bindStaticTag ts = do - sr <- newIORef $ Set.empty - mv <- liftM STS $ newMVar Map.empty - - return $ (addOnLoadHook (assignIds sr) $ - bindSplice "static" (staticImpl mv) ts, - mv) - - where - generateId :: IO Int - generateId = getStdRandom random - - assignIds setref = mapM f - where - f node = g $ fromTree node - - getId = do - i <- liftM (B.pack . show) generateId - st <- readIORef setref - if Set.member i st - then getId - else do - writeIORef setref $ Set.insert i st - return i - - g curs = do - let node = current curs - curs' <- if getName node == "static" - then do - i <- getId - return $ modifyContent (setAttribute "id" i) curs - else return curs - let mbc = nextDF curs' - maybe (return $ toTree curs') g mbc - - - - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices.html deleted file mode 100644 index 9919a2e..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist-Splices.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
module Text.Templating.Heist.Splices - ( module Text.Templating.Heist.Splices.Apply - , module Text.Templating.Heist.Splices.Bind - , module Text.Templating.Heist.Splices.Ignore - , module Text.Templating.Heist.Splices.Markdown - , module Text.Templating.Heist.Splices.Static - ) where - -import Text.Templating.Heist.Splices.Apply -import Text.Templating.Heist.Splices.Bind -import Text.Templating.Heist.Splices.Ignore -import Text.Templating.Heist.Splices.Markdown -import Text.Templating.Heist.Splices.Static - -- diff --git a/static/docs/0.1.1/heist/src/Text-Templating-Heist.html b/static/docs/0.1.1/heist/src/Text-Templating-Heist.html deleted file mode 100644 index e38d59b..0000000 --- a/static/docs/0.1.1/heist/src/Text-Templating-Heist.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} - -{-| - - This module contains the core definitions for the Heist template system. - - The Heist template system is based on XML\/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - - The most important concept in Heist is the 'Splice'. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. 'Splice' is implemented as a type synonym @type Splice m = - TemplateMonad m [Node]@, and 'TemplateMonad' has a function 'getParamNode' - that lets you get the input node. - - Suppose you have a place on your page where you want to display a link with - the text \"Logout username\" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - @getUser :: MyAppMonad (Maybe ByteString)@ that gets the current user. - You can implement this functionality with a 'Splice' as follows: - - > - > import Text.XML.Expat.Tree - > - > link :: ByteString -> ByteString -> Node - > link target text = X.Element "a" [("href", target)] [X.Text text] - > - > loginLink :: Node - > loginLink = link "/login" "Login" - > - > logoutLink :: ByteString -> Node - > logoutLink user = link "/logout" (B.append "Logout " user) - > - > loginLogoutSplice :: Splice MyAppMonad - > loginLogoutSplice = do - > user <- lift getUser - > return $ [maybe loginLink logoutLink user] - > - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the 'TemplateState' data structure. The - following code demonstrates how this splice would be used. - - > mySplices = [ ("loginLogout", loginLogoutSplice) ] - > - > main = do - > ets <- loadTemplates "templates" $ - > foldr (uncurry bindSplice) emptyTemplateState mySplices - > let ts = either error id ets - > t <- runMyAppMonad $ renderTemplate ts "index" - > print $ maybe "Page not found" id t - - Here we build up our 'TemplateState' by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final 'TemplateState' wrapped in an Either to handle - errors. Then we use this 'TemplateState' to render our templates. - --} - -module Text.Templating.Heist - ( - -- * Types - Node - , Splice - , Template - , TemplateMonad - , TemplateState - - -- * Functions and declarations on TemplateState values - , addTemplate - , emptyTemplateState - , bindSplice - , lookupSplice - , setTemplates - , loadTemplates - - -- * Hook functions - -- $hookDoc - , addOnLoadHook - , addPreRunHook - , addPostRunHook - - -- * TemplateMonad functions - , stopRecursion - , getParamNode - , runNodeList - , getContext - - -- * Functions for running splices and templates - , runTemplate - , evalTemplate - , callTemplate - , renderTemplate - , bindStrings - - -- * Misc functions - , runSplice - , runRawTemplate - , getDoc - , bindStaticTag - - , heistExpatOptions - , module Text.Templating.Heist.Constants - ) where - -import Control.Monad.Trans -import qualified Data.Map as Map -import Text.Templating.Heist.Internal -import Text.Templating.Heist.Constants -import Text.Templating.Heist.Splices - - ------------------------------------------------------------------------------- --- | The default set of built-in splices. -defaultSpliceMap :: MonadIO m => SpliceMap m -defaultSpliceMap = Map.fromList - [(applyTag, applyImpl) - ,(bindTag, bindImpl) - ,(ignoreTag, ignoreImpl) - ,(markdownTag, markdownSplice) - ] - - ------------------------------------------------------------------------------- --- | An empty template state, with Heist's default splices (@\<bind\>@ and --- @\<apply\>@) mapped. -emptyTemplateState :: MonadIO m => TemplateState m -emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 - return return return - - ------------------------------------------------------------------------------- --- | Reloads the templates from disk and renders the specified --- template. (Old convenience code.) ---renderTemplate' :: FilePath -> ByteString -> IO (Maybe ByteString) ---renderTemplate' baseDir name = do --- etm <- loadTemplates baseDir emptyTemplateState --- let ts = either (const emptyTemplateState) id etm --- ns <- runTemplate ts name --- return $ (Just . formatList') =<< ns - - --- $hookDoc --- Heist hooks allow you to modify templates when they are loaded and before --- and after they are run. Every time you call one of the addAbcHook --- functions the hook is added to onto the processing pipeline. The hooks --- processes the template in the order that they were added to the --- TemplateState. --- --- The pre-run and post-run hooks are run before and after every template is --- run/rendered. You should be careful what code you put in these hooks --- because it can significantly affect the performance of your site. - -- diff --git a/static/docs/0.1.1/heist/src/hscolour.css b/static/docs/0.1.1/heist/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.1/heist/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.1.1/snap-core/Data-CIByteString.html b/static/docs/0.1.1/snap-core/Data-CIByteString.html deleted file mode 100644 index 45f6c03..0000000 --- a/static/docs/0.1.1/snap-core/Data-CIByteString.html +++ /dev/null @@ -1,324 +0,0 @@ - - -
| |||||
| |||||
Description | |||||
Data.CIByteString is a module containing CIByteString, a wrapper for - ByteString which provides case-insensitive (ASCII-wise) Ord and Eq - instances. - CIByteString also has an IsString instance, so if you use the - "OverloadedStrings" LANGUAGE pragma you can write case-insensitive string - literals, e.g.: - - > let a = "Foo" in - putStrLn $ (show $ unCI a) ++ "==\"FoO\" is " ++ show (a == "FoO") - "Foo"=="FoO" is True - | |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for (optionally) printing debugging - messages. Normally debug does nothing, but you can pass "-fdebug" to - cabal install to build a snap-core which debugs to stderr. - N.B. this is an internal interface, please don't write external code that - depends on it. - | |||||
Documentation | |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An internal Snap module containing HTTP types. - N.B. this is an internal interface, please don't write user code that - depends on it. Most of these declarations (except for the - unsafe/encapsulation-breaking ones) are re-exported from Snap.Types. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Documentation | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets all of the values for a given header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for debugging iteratees. - N.B. this is an internal interface, please don't write user code that - depends on it. - | |||||
Documentation | |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||
Snap Framework type aliases and utilities for iteratees. Note that as a - convenience, this module also exports everything from Data.Iteratee in the - iteratee library. - WARNING: Note that all of these types are scheduled to change in the - darcs head version of the iteratee library; John Lato et al. are working - on a much improved iteratee formulation. - | |||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Convenience aliases around types from Data.Iteratee - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Re-export types and functions from Data.Iteratee - | |||||||||||||||||||||||||||
module Data.Iteratee | |||||||||||||||||||||||||||
Helper functions - | |||||||||||||||||||||||||||
Enumerators - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a strict bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Conversion to/from WrappedByteString - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a wrapped bytestring to a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a lazy bytestring to a wrapped bytestring. - | |||||||||||||||||||||||||||
Iteratee utilities - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads n elements from a stream and applies the given iteratee to - the stream of the read elements. Reads exactly n elements, and if - the stream is short propagates an error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads up to n elements from a stream and applies the given iteratee to the - stream of the read elements. If more than n elements are read, propagates an - error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Wraps an Iteratee, counting the number of bytes consumed by it. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Buffers an iteratee. - Our enumerators produce a lot of little strings; rather than spending all - our time doing kernel context switches for 4-byte write() calls, we buffer - the iteratee to send 2KB at a time. - | |||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core type definitions, class instances, and functions -for HTTP as well as the Snap monad, which is used for web handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Snap Monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action in the 'Iteratee IO' monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for control flow and early termination - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Short-circuits a Snap monad action early, storing the given - Response value in its state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fails out of a Snap monad action. This is used to indicate - that you choose not to handle the given request within the given - handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Routing - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only if the request's HTTP method matches - the given method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only when rqPathInfo is empty. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A web handler which, given a mapping from URL entry points to web - handlers, efficiently routes requests to the correct handler. - The URL entry points are given as relative paths, for example: - route [ ("foo/bar/quux", fooBarQuux) ] - If the URI of the incoming request is - /foo/bar/quux - or - /foo/bar/quux/...anything... - then the request will be routed to "fooBarQuux", with rqContextPath - set to "/foo/bar/quux/" and rqPathInfo set to - "...anything...". - FIXME/TODO: we need a version with and without the context path setting - behaviour; if the route is "article/:id/print", we probably want the - contextPath to be "/article" instead of "/article/whatever/print". - A path component within an URL entry point beginning with a colon (":") - is treated as a variable capture; the corresponding path component within - the request URI will be entered into the rqParams parameters mapping with - the given name. For instance, if the routes were: - route [ ("foo/:bar/baz", fooBazHandler) ] - Then a request for "/foo/saskatchewan/baz" would be routed to - fooBazHandler with a mapping for: - "bar" => "saskatchewan" - in its parameters table. - Longer paths are matched first, and specific routes are matched before - captures. That is, if given routes: - [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] - a request for "/a/b" will go to h2, "/a/s" for any s will go - to h3, and "/a" will go to h1. - The following example matches "/article" to an article index, - "/login" to a login, and "/article/..." to an article renderer. - route [ ("article", renderIndex) - , ("article/:id", renderArticle) - , ("login", method POST doLogin) ] - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The routeLocal function is the same as route, except it doesn't change - the request's context path. This is useful if you want to route to a - particular handler but you want that handler to receive the rqPathInfo as - it is. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Access to state - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Request object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Response object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Request object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Response object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the Request object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifes the Response object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap action with a locally-modified Request state - object. The Request object in the Snap monad state after the call - to localRequest will be unchanged. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Request from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Response from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabbing request bodies - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sends the request body through an iteratee (data consumer) and - returns the result. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the request body as a bytestring. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Detaches the request body's Enumerator from the Request and - returns it. You would want to use this if you needed to send the - HTTP request body (transformed or otherwise) through to the output - in O(1) space. (Examples: transcoding, "echo", etc) - Normally Snap is careful to ensure that the request body is fully - consumed after your web handler runs; this function is marked - "unsafe" because it breaks this guarantee and leaves the - responsibility up to you. If you don't fully consume the - Enumerator you get here, the next HTTP request in the pipeline - (if any) will misparse. Be careful with exception handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP Datatypes and Functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP-related datatypes: Request, Response, Cookie, etc. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Headers - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Requests - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The server name of the request, as it came in from the request's - Host: header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the port number the HTTP server is listening on. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote IP address. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote TCP port number. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The local IP address for this request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP server's idea of its local hostname. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns True if this is an HTTPS session (currently always - False). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Content-Length of the HTTP request body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP request method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP version used by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns a list of the cookies that came in from the HTTP request - headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Handlers can (will be; --ed) be hung on a URI "entry point"; - this is called the "context path". If a handler is hung on the - context path "/foo/", and you request "/foo/bar", the value - of rqPathInfo will be "bar". - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The "context path" of the request; catenating rqContextPath, and - rqPathInfo should get you back to the original rqURI. The - rqContextPath always begins and ends with a slash ("/") - character, and represents the path (relative to your - component/snaplet) you took to get to your handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the URI requested by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP query string for this Request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Params mapping for this Request. "Parameters" are - automatically decoded from the query string and POST body and - entered into this mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Responses - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status code. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status explanation string. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Response I/O - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the output to be the contents of the specified file. - Calling sendFile will overwrite any output queued to be sent in the - Response. If the response body is not modified after the call to - sendFile, Snap will use the efficient sendfile() system call on - platforms that support it. - If the response body is modified (using modifyResponseBody), the file will - be read using mmap(). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Iteratee - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP utilities - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Description | |||||||||||||
Contains web handlers to serve files from a directory. - | |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
Gets a path from the Request using rqPathInfo and makes sure it is - safe to use for opening files. A path is safe if it is a relative path - and has no .. elements to escape the intended directory structure. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
The default set of mime type mappings we use when serving files. Its - value: - Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - | |||||||||||||
| |||||||||||||
A type alias for MIME type - | |||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||
| ||||||||
Synopsis | ||||||||
| ||||||||
Documentation | ||||||||
| ||||||||
| ||||||||
| ||||||||
| ||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (>) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (A) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (B) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||
Index (C) | ||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (D) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
Index (E) | ||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||
Index (F) | ||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (G) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (H) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (I) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (J) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (L) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (M) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (N) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (O) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||
| ||||||||||||||||||||||||
Index (P) | ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Index (R) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||
Index (S) | ||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (T) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (U) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (W) | |||||||||||||||||||||
|
| |||||||||||||||||||||
Index | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
snap-core-0.1.1: Snap: A Haskell Web Framework (Core) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - This library contains the core definitions and types for the Snap framework, -including: - 1. Primitive types and functions for HTTP (requests, responses, cookies, -post/query parameters, etc) - 2. Type aliases and helper functions for Iteratee I/O - 3. A monad for programming web handlers called "Snap", inspired by -happstack's (http://happstack.com/index.html), which allows: -
Quick start: The Snap monad and HTTP definitions are in Snap.Types, -some iteratee utilities are in Snap.Iteratee. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - --- | "Data.CIByteString" is a module containing 'CIByteString', a wrapper for --- 'ByteString' which provides case-insensitive (ASCII-wise) 'Ord' and 'Eq' --- instances. --- --- 'CIByteString' also has an 'IsString' instance, so if you use the --- \"OverloadedStrings\" LANGUAGE pragma you can write case-insensitive string --- literals, e.g.: --- --- @ --- \> let a = \"Foo\" in --- putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++ show (a == \"FoO\") --- \"Foo\"==\"FoO\" is True --- @ - -module Data.CIByteString - ( CIByteString - , toCI - , unCI - ) where - --- for IsString instance -import Data.ByteString.Char8 () -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString as S -import Data.Char -import Data.String - - --- | A case-insensitive newtype wrapper for 'ByteString' -data CIByteString = CIByteString { unCI :: !ByteString - , _lowercased :: !ByteString } - -toCI :: ByteString -> CIByteString -toCI s = CIByteString s t - where - t = lowercase s - -instance Show CIByteString where - show (CIByteString s _) = show s - -lowercase :: ByteString -> ByteString -lowercase = S.map (c2w . toLower . w2c) - -instance Eq CIByteString where - (CIByteString _ a) == (CIByteString _ b) = a == b - (CIByteString _ a) /= (CIByteString _ b) = a /= b - -instance Ord CIByteString where - (CIByteString _ a) <= (CIByteString _ b) = a <= b - -instance IsString CIByteString where - fromString = toCI . fromString -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Internal-Debug.html b/static/docs/0.1.1/snap-core/src/Snap-Internal-Debug.html deleted file mode 100644 index c295589..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Internal-Debug.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - -
-- | An internal Snap module for (optionally) printing debugging --- messages. Normally 'debug' does nothing, but you can pass \"-fdebug\" to --- @cabal install@ to build a @snap-core@ which debugs to stderr. --- --- /N.B./ this is an internal interface, please don't write external code that --- depends on it. - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} - -module Snap.Internal.Debug where - -import Control.Monad.Trans - -#ifdef DEBUG_TEST - -debug :: (MonadIO m) => String -> m () -debug !s = return $ s `seq` () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno !s = return $ s `seq` () - -#elif defined(DEBUG) - ------------------------------------------------------------------------------- -import Control.Concurrent -import Data.List -import Data.Maybe -import Foreign.C.Error -import System.IO -import System.IO.Unsafe -import Text.Printf ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -_debugMVar :: MVar () -_debugMVar = unsafePerformIO $ newMVar () - - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug s = liftIO $ withMVar _debugMVar $ \_ -> do - tid <- myThreadId - hPutStrLn stderr $ s' tid - hFlush stderr - where - chop x = let y = fromMaybe x $ stripPrefix "ThreadId " x - in printf "%8s" y - - s' t = "[" ++ chop (show t) ++ "] " ++ s - -{-# INLINE debug #-} - - ------------------------------------------------------------------------------- -debugErrno :: (MonadIO m) => String -> m () -debugErrno loc = liftIO $ do - err <- getErrno - let ex = errnoToIOError loc err Nothing Nothing - debug $ show ex ------------------------------------------------------------------------------- - -#else - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug _ = return () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno _ = return () ------------------------------------------------------------------------------- - -#endif -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Internal-Http-Types.html b/static/docs/0.1.1/snap-core/src/Snap-Internal-Http-Types.html deleted file mode 100644 index 44d3120..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Internal-Http-Types.html +++ /dev/null @@ -1,648 +0,0 @@ - - - - -
-- | An internal Snap module containing HTTP types. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. Most of these declarations (except for the --- unsafe/encapsulation-breaking ones) are re-exported from "Snap.Types". - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeSynonymInstances #-} - -module Snap.Internal.Http.Types where - - ------------------------------------------------------------------------------- -import Control.Applicative hiding (empty) -import Control.Monad (liftM, when) -import qualified Data.Attoparsec as Atto -import Data.Attoparsec hiding (many, Result(..)) -import Data.Bits -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w,w2c) -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import qualified Data.ByteString as S -import Data.Char -import Data.DList (DList) -import qualified Data.DList as DL -import Data.IORef -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Monoid -import Data.Serialize.Builder -import Data.Time.Clock -import Data.Time.Format -import Data.Word -import Foreign hiding (new) -import Foreign.C.String -import Foreign.C.Types -import Prelude hiding (take) -import System.Locale (defaultTimeLocale) - ------------------------------------------------------------------------------- -import Data.CIByteString -import qualified Snap.Iteratee as I - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "set_c_locale" - set_c_locale :: IO () - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_parse_http_time" - c_parse_http_time :: CString -> IO CTime - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_format_http_time" - c_format_http_time :: CTime -> CString -> IO () - ------------------------------------------------------------------------------- -type Enumerator a = I.Enumerator IO a - ------------------------------------------------------------------------------- --- | A type alias for a case-insensitive key-value mapping. -type Headers = Map CIByteString [ByteString] - - ------------------------------------------------------------------------------- --- | A typeclass for datatypes which contain HTTP headers. -class HasHeaders a where - - -- | Modify the datatype's headers. - updateHeaders :: (Headers -> Headers) -> a -> a - - -- | Retrieve the headers from a datatype that has headers. - headers :: a -> Headers - - ------------------------------------------------------------------------------- --- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header with --- the same name already exists, the new value is appended to the headers list. -addHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -addHeader k v = updateHeaders $ Map.insertWith' (++) k [v] - - ------------------------------------------------------------------------------- --- | Sets a header key-value-pair in a 'HasHeaders' datatype. If a header with --- the same name already exists, it is overwritten with the new value. -setHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -setHeader k v = updateHeaders $ Map.insert k [v] - - ------------------------------------------------------------------------------- --- | Gets all of the values for a given header. -getHeaders :: (HasHeaders a) => CIByteString -> a -> Maybe [ByteString] -getHeaders k a = Map.lookup k $ headers a - - ------------------------------------------------------------------------------- --- | Gets a header value out of a 'HasHeaders' datatype. If many headers came --- in with the same name, they will be catenated together. -getHeader :: (HasHeaders a) => CIByteString -> a -> Maybe ByteString -getHeader k a = liftM (S.intercalate " ") (Map.lookup k $ headers a) - - ------------------------------------------------------------------------------- --- | Enumerates the HTTP method values (see --- <http://tools.ietf.org/html/rfc2068.html#section-5.1.1>). -data Method = GET | HEAD | POST | PUT | DELETE | TRACE | OPTIONS | CONNECT - deriving(Show,Read,Ord,Eq) - - ------------------------------------------------------------------------------- -type HttpVersion = (Int,Int) - - ------------------------------------------------------------------------------- --- | A datatype representing an HTTP cookie. -data Cookie = Cookie { - -- | The name of the cookie. - cookieName :: !ByteString - - -- | The cookie's string value. - , cookieValue :: !ByteString - - -- | The cookie's expiration value, if it has one. - , cookieExpires :: !(Maybe UTCTime) - - -- | The cookie's \"domain\" value, if it has one. - , cookieDomain :: !(Maybe ByteString) - - -- | The cookie path. - , cookiePath :: !(Maybe ByteString) -} deriving (Eq, Show) - - ------------------------------------------------------------------------------- --- | A type alias for the HTTP parameters mapping. Each parameter --- key maps to a list of ByteString values; if a parameter is specified --- multiple times (e.g.: \"@GET /foo?param=bar1¶m=bar2@\"), looking up --- \"@param@\" in the mapping will give you @[\"bar1\", \"bar2\"]@. -type Params = Map ByteString [ByteString] - - ------------------------------------------------------------------------------- --- request type ------------------------------------------------------------------------------- - -data SomeEnumerator = SomeEnumerator (forall a . Enumerator a) - - ------------------------------------------------------------------------------- --- | Contains all of the information about an incoming HTTP request. -data Request = Request - { -- | The server name of the request, as it came in from the request's - -- @Host:@ header. - rqServerName :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqServerPort :: !Int - - -- | The remote IP address. - , rqRemoteAddr :: !ByteString - - -- | The remote TCP port number. - , rqRemotePort :: !Int - - -- | The local IP address for this request. - , rqLocalAddr :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqLocalPort :: !Int - - -- | Returns the HTTP server's idea of its local hostname. - , rqLocalHostname :: !ByteString - - -- | Returns @True@ if this is an @HTTPS@ session (currently always - -- @False@). - , rqIsSecure :: !Bool - , rqHeaders :: Headers - , rqBody :: IORef SomeEnumerator - - -- | Returns the @Content-Length@ of the HTTP request body. - , rqContentLength :: !(Maybe Int) - - -- | Returns the HTTP request method. - , rqMethod :: !Method - - -- | Returns the HTTP version used by the client. - , rqVersion :: !HttpVersion - - -- | Returns a list of the cookies that came in from the HTTP request - -- headers. - , rqCookies :: [Cookie] - - - -- | We'll be doing web components (or \"snaplets\") for version 0.2. The - -- \"snaplet path\" refers to the place on the URL where your containing - -- snaplet is hung. The value of 'rqSnapletPath' is either @\"\"@ (at the - -- top-level context) or is a path beginning with a slash, but not ending - -- with one. - -- - -- An identity is that: - -- - -- > rqURI r == 'S.concat' [ rqSnapletPath r - -- > , rqContextPath r - -- > , rqPathInfo r ] - -- - -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will be - -- \"\" - , rqSnapletPath :: !ByteString - - -- | Handlers can (/will be; --ed/) be hung on a @URI@ \"entry point\"; - -- this is called the \"context path\". If a handler is hung on the - -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the value - -- of 'rqPathInfo' will be @\"bar\"@. - , rqPathInfo :: !ByteString - - -- | The \"context path\" of the request; catenating 'rqContextPath', and - -- 'rqPathInfo' should get you back to the original 'rqURI'. The - -- 'rqContextPath' always begins and ends with a slash (@\"\/\"@) - -- character, and represents the path (relative to your - -- component\/snaplet) you took to get to your handler. - , rqContextPath :: !ByteString - - -- | Returns the @URI@ requested by the client. - , rqURI :: !ByteString - - -- | Returns the HTTP query string for this 'Request'. - , rqQueryString :: !ByteString - - -- | Returns the 'Params' mapping for this 'Request'. \"Parameters\" are - -- automatically decoded from the query string and @POST@ body and - -- entered into this mapping. - , rqParams :: Params - } - - ------------------------------------------------------------------------------- -instance Show Request where - show r = concat [ "Request <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - sname - , remote - , local - , beginheaders - , hdrs - , endheaders - , contentlength - , method - , version - , cookies - , pathinfo - , contextpath - , snapletpath - , uri - , params - ] - - sname = concat [ "server-name: ", toStr $ rqServerName r ] - remote = concat [ "remote: " - , toStr $ rqRemoteAddr r - , ":" - , show (rqRemotePort r) - ] - local = concat [ "local: " - , toStr $ rqLocalAddr r - , ":" - , show $ rqServerPort r - ] - beginheaders = "Headers:\n ========================================" - endheaders = " ========================================" - hdrs = " " ++ show (rqHeaders r) - contentlength = concat [ "content-length: " - , show $ rqContentLength r - ] - method = concat [ "method: " - , show $ rqMethod r - ] - version = concat [ "version: " - , show $ rqVersion r - ] - cookies = concat [ "cookies:\n" - , " ========================================\n" - , " " ++ (show $ rqCookies r) - , "\n ========================================" - ] - pathinfo = concat [ "pathinfo: ", toStr $ rqPathInfo r ] - contextpath = concat [ "contextpath: ", toStr $ rqContextPath r ] - snapletpath = concat [ "snapletpath: ", toStr $ rqSnapletPath r ] - uri = concat [ "URI: ", toStr $ rqURI r ] - params = concat [ "params:\n" - , " ========================================\n" - , " " ++ (show $ rqParams r) - , "\n ========================================" - ] - - ------------------------------------------------------------------------------- -instance HasHeaders Request where - headers = rqHeaders - updateHeaders f r = r { rqHeaders = f (rqHeaders r) } - - ------------------------------------------------------------------------------- -instance HasHeaders Headers where - headers = id - updateHeaders = id - ------------------------------------------------------------------------------- --- response type ------------------------------------------------------------------------------- - -data ResponseBody = Enum (forall a . Enumerator a) -- ^ output body is enumerator - | SendFile FilePath -- ^ output body is sendfile() - - ------------------------------------------------------------------------------- -rspBodyMap :: (forall a . Enumerator a -> Enumerator a) - -> ResponseBody - -> ResponseBody -rspBodyMap f b = Enum $ f $ rspBodyToEnum b - - ------------------------------------------------------------------------------- -rspBodyToEnum :: ResponseBody -> Enumerator a -rspBodyToEnum (Enum e) = e -rspBodyToEnum (SendFile fp) = I.enumFile fp - - ------------------------------------------------------------------------------- --- | Represents an HTTP response. -data Response = Response - { rspHeaders :: Headers - , rspHttpVersion :: !HttpVersion - - -- | We will need to inspect the content length no matter what, and - -- looking up \"content-length\" in the headers and parsing the number - -- out of the text will be too expensive. - , rspContentLength :: !(Maybe Int) - , rspBody :: ResponseBody - - -- | Returns the HTTP status code. - , rspStatus :: !Int - - -- | Returns the HTTP status explanation string. - , rspStatusReason :: !ByteString - } - - ------------------------------------------------------------------------------- -instance Show Response where - show r = concat [ "Response <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - hdrs - , version - , status - , reason - ] - - hdrs = concat [ "headers:\n" - , " ==============================\n " - , show $ rspHeaders r - , "\n ==============================" ] - - version = concat [ "version: ", show $ rspHttpVersion r ] - status = concat [ "status: ", show $ rspStatus r ] - reason = concat [ "reason: ", toStr $ rspStatusReason r ] - - ------------------------------------------------------------------------------- -instance HasHeaders Response where - headers = rspHeaders - updateHeaders f r = r { rspHeaders = f (rspHeaders r) } - - ------------------------------------------------------------------------------- --- | Looks up the value(s) for the given named parameter. Parameters initially --- come from the request's query string and any decoded POST body (if the --- request's @Content-Type@ is @application\/x-www-form-urlencoded@). Parameter --- values can be modified within handlers using "rqModifyParams". -rqParam :: ByteString -- ^ parameter name to look up - -> Request -- ^ HTTP request - -> Maybe [ByteString] -rqParam k rq = Map.lookup k $ rqParams rq -{-# INLINE rqParam #-} - - ------------------------------------------------------------------------------- --- | Modifies the parameters mapping (which is a @Map ByteString ByteString@) in --- a 'Request' using the given function. -rqModifyParams :: (Params -> Params) -> Request -> Request -rqModifyParams f r = r { rqParams = p } - where - p = f $ rqParams r -{-# INLINE rqModifyParams #-} - - ------------------------------------------------------------------------------- --- | Writes a key-value pair to the parameters mapping within the given request. -rqSetParam :: ByteString -- ^ parameter name - -> [ByteString] -- ^ parameter values - -> Request -- ^ request - -> Request -rqSetParam k v = rqModifyParams $ Map.insert k v -{-# INLINE rqSetParam #-} - ------------------------------------------------------------------------------- --- responses ------------------------------------------------------------------------------- - --- | An empty 'Response'. -emptyResponse :: Response -emptyResponse = Response Map.empty (1,1) Nothing (Enum return) 200 "OK" - - ------------------------------------------------------------------------------- --- | Sets an HTTP response body to the given 'Enumerator' value. -setResponseBody :: (forall a . Enumerator a) -- ^ new response body - -- enumerator - -> Response -- ^ response to modify - -> Response -setResponseBody e r = r { rspBody = Enum e } -{-# INLINE setResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the HTTP response status. -setResponseStatus :: Int -- ^ HTTP response integer code - -> ByteString -- ^ HTTP response explanation - -> Response -- ^ Response to be modified - -> Response -setResponseStatus s reason r = r { rspStatus=s, rspStatusReason=reason } -{-# INLINE setResponseStatus #-} - - ------------------------------------------------------------------------------- --- | Modifies a response body. -modifyResponseBody :: (forall a . Enumerator a -> Enumerator a) - -> Response - -> Response -modifyResponseBody f r = r { rspBody = rspBodyMap f (rspBody r) } -{-# INLINE modifyResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the @Content-Type@ in the 'Response' headers. -setContentType :: ByteString -> Response -> Response -setContentType = setHeader "Content-Type" -{-# INLINE setContentType #-} - - ------------------------------------------------------------------------------- --- | Adds an HTTP 'Cookie' to the 'Response' headers. -addCookie :: Cookie -- ^ cookie value - -> Response -- ^ response to modify - -> Response -addCookie (Cookie k v mbExpTime mbDomain mbPath) = updateHeaders f - where - f = Map.insertWith' (++) "Set-Cookie" [cookie] - cookie = S.concat [k, "=", v, path, exptime, domain] - path = maybe "" (S.append "; path=") mbPath - domain = maybe "" (S.append "; domain=") mbDomain - exptime = maybe "" (S.append "; expires=" . fmt) mbExpTime - fmt = fromStr . formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT" - - ------------------------------------------------------------------------------- --- | A note here: if you want to set the @Content-Length@ for the response, --- Snap forces you to do it with this function rather than by setting it in the --- headers; the @Content-Length@ in the headers will be ignored. --- --- The reason for this is that Snap needs to look up the value of --- @Content-Length@ for each request, and looking the string value up in the --- headers and parsing the number out of the text will be too expensive. --- --- If you don't set a content length in your response, HTTP keep-alive will be --- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For HTTP\/1.1 --- clients, Snap will switch to the chunked transfer encoding if --- @Content-Length@ is not specified. -setContentLength :: Int -> Response -> Response -setContentLength l r = r { rspContentLength = Just l } -{-# INLINE setContentLength #-} - - ------------------------------------------------------------------------------- --- | Removes any @Content-Length@ set in the 'Response'. -clearContentLength :: Response -> Response -clearContentLength r = r { rspContentLength = Nothing } -{-# INLINE clearContentLength #-} - - ------------------------------------------------------------------------------- --- HTTP dates - -{- --- | Converts a 'ClockTime' into an HTTP timestamp. -formatHttpTime :: UTCTime -> ByteString -formatHttpTime = fromStr . formatTime defaultTimeLocale "%a, %d %b %Y %X GMT" - --- | Converts an HTTP timestamp into a 'UTCTime'. -parseHttpTime :: ByteString -> Maybe UTCTime -parseHttpTime s' = - parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" s - where - s = toStr s' --} - --- | Converts a 'CTime' into an HTTP timestamp. -formatHttpTime :: CTime -> IO ByteString -formatHttpTime t = allocaBytes 40 $ \ptr -> do - c_format_http_time t ptr - S.packCString ptr - - ------------------------------------------------------------------------------- --- | Converts an HTTP timestamp into a 'CTime'. -parseHttpTime :: ByteString -> IO CTime -parseHttpTime s = S.useAsCString s $ \ptr -> - c_parse_http_time ptr - - ------------------------------------------------------------------------------- --- URL ENCODING ------------------------------------------------------------------------------- - -parseToCompletion :: Parser a -> ByteString -> Maybe a -parseToCompletion p s = toResult $ finish r - where - r = parse p s - - toResult (Atto.Done _ c) = Just c - toResult _ = Nothing - - ------------------------------------------------------------------------------- -pUrlEscaped :: Parser ByteString -pUrlEscaped = do - sq <- nextChunk DL.empty - return $ S.concat $ DL.toList sq - - where - nextChunk :: DList ByteString -> Parser (DList ByteString) - nextChunk s = (endOfInput *> pure s) <|> do - c <- anyWord8 - case w2c c of - '+' -> plusSpace s - '%' -> percentEncoded s - _ -> unEncoded c s - - percentEncoded :: DList ByteString -> Parser (DList ByteString) - percentEncoded l = do - hx <- take 2 - when (S.length hx /= 2 || - (not $ S.all (isHexDigit . w2c) hx)) $ - fail "bad hex in url" - - let code = (Cvt.hex hx) :: Word8 - nextChunk $ DL.snoc l (S.singleton code) - - unEncoded :: Word8 -> DList ByteString -> Parser (DList ByteString) - unEncoded c l' = do - let l = DL.snoc l' (S.singleton c) - bs <- takeTill (flip elem (map c2w "%+")) - if S.null bs - then nextChunk l - else nextChunk $ DL.snoc l bs - - plusSpace :: DList ByteString -> Parser (DList ByteString) - plusSpace l = nextChunk (DL.snoc l (S.singleton $ c2w ' ')) - - ------------------------------------------------------------------------------- --- | Decodes an URL-escaped string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlDecode :: ByteString -> Maybe ByteString -urlDecode = parseToCompletion pUrlEscaped - - ------------------------------------------------------------------------------- --- "...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," --- [not including the quotes - ed], and reserved characters used for their --- reserved purposes may be used unencoded within a URL." - --- | URL-escapes a string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlEncode :: ByteString -> ByteString -urlEncode = toByteString . S.foldl' f empty - where - f b c = - if c == c2w ' ' - then b `mappend` singleton (c2w '+') - else if isKosher c - then b `mappend` singleton c - else b `mappend` hexd c - - isKosher w = any ($ c) [ isAlphaNum - , flip elem ['$', '-', '.', '!', '*' - , '\'', '(', ')', ',' ]] - where - c = w2c w - - ------------------------------------------------------------------------------- -hexd :: Word8 -> Builder -hexd c = singleton (c2w '%') `mappend` singleton hi `mappend` singleton low - where - d = c2w . intToDigit - low = d $ fromEnum $ c .&. 0xf - hi = d $ fromEnum $ (c .&. 0xf0) `shift` (-4) - - ------------------------------------------------------------------------------- -finish :: Atto.Result a -> Atto.Result a -finish (Atto.Partial f) = flip feed "" $ f "" -finish x = x - - ------------------------------------------------------------------------------- --- local definitions -fromStr :: String -> ByteString -fromStr = S.pack . map c2w -{-# INLINE fromStr #-} - ------------------------------------------------------------------------------- --- private helper functions -toStr :: ByteString -> String -toStr = map w2c . S.unpack - -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Internal-Iteratee-Debug.html b/static/docs/0.1.1/snap-core/src/Snap-Internal-Iteratee-Debug.html deleted file mode 100644 index 972fc9d..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Internal-Iteratee-Debug.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
-- | An internal Snap module for debugging iteratees. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. - -{-# OPTIONS_GHC -fno-warn-orphans #-} -{-# LANGUAGE FlexibleInstances #-} - -module Snap.Internal.Iteratee.Debug ( debugIteratee ) where - ------------------------------------------------------------------------------- -import Data.Iteratee.WrappedByteString -import Data.Word (Word8) -import System.IO ------------------------------------------------------------------------------- -import Snap.Iteratee ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -instance Show (WrappedByteString Word8) where - show (WrapBS s) = show s - - ------------------------------------------------------------------------------- -debugIteratee :: Iteratee IO () -debugIteratee = IterateeG f - where - f c@(EOF _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return (Done () c) - - f c@(Chunk _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return $ Cont debugIteratee Nothing -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Internal-Routing.html b/static/docs/0.1.1/snap-core/src/Snap-Internal-Routing.html deleted file mode 100644 index ab4539e..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Internal-Routing.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - -
module Snap.Internal.Routing where - - ------------------------------------------------------------------------------- -import Control.Applicative ((<|>)) -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.Monoid -import qualified Data.Map as Map - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Types - - ------------------------------------------------------------------------------- -{-| - -The internal data type you use to build a routing tree. Matching is -done unambiguously. - -'Capture' and 'Dir' routes can have a "fallback" route: - - - For 'Capture', the fallback is routed when there is nothing to capture - - For 'Dir', the fallback is routed when we can't find a route in its map - -Fallback routes are stacked: i.e. for a route like: - -> Dir [("foo", Capture "bar" (Action bar) NoRoute)] baz - -visiting the URI foo/ will result in the "bar" capture being empty and -triggering its fallback. It's NoRoute, so we go to the nearest parent -fallback and try that, which is the baz action. - --} -data Route a = Action (Snap a) -- wraps a 'Snap' action - | Capture ByteString (Route a) (Route a) -- captures the dir in a param - | Dir (Map.Map ByteString (Route a)) (Route a) -- match on a dir - | NoRoute - - ------------------------------------------------------------------------------- -instance Monoid (Route a) where - mempty = NoRoute - - -- Unions two routes, favoring the right-hand side - mappend NoRoute r = r - - mappend l@(Action _) r = case r of - (Action _) -> r - (Capture p r' fb) -> Capture p r' (mappend fb l) - (Dir _ _) -> mappend (Dir Map.empty l) r - NoRoute -> l - - mappend l@(Capture p r' fb) r = case r of - (Action _) -> Capture p r' (mappend fb r) - (Capture p' r'' fb') - | p == p' -> Capture p (mappend r' r'') (mappend fb fb') - | otherwise -> r - (Dir rm fb') -> Dir rm (mappend fb' l) - NoRoute -> l - - mappend l@(Dir rm fb) r = case r of - (Action _) -> Dir rm (mappend fb r) - (Capture _ _ _) -> Dir rm (mappend fb r) - (Dir rm' fb') -> Dir (Map.unionWith mappend rm rm') (mappend fb fb') - NoRoute -> l - - ------------------------------------------------------------------------------- --- | A web handler which, given a mapping from URL entry points to web --- handlers, efficiently routes requests to the correct handler. --- --- The URL entry points are given as relative paths, for example: --- --- > route [ ("foo/bar/quux", fooBarQuux) ] --- --- If the URI of the incoming request is --- --- > /foo/bar/quux --- --- or --- --- > /foo/bar/quux/...anything... --- --- then the request will be routed to \"@fooBarQuux@\", with 'rqContextPath' --- set to \"@\/foo\/bar\/quux\/@\" and 'rqPathInfo' set to --- \"@...anything...@\". --- --- @FIXME@\/@TODO@: we need a version with and without the context path setting --- behaviour; if the route is \"@article\/:id\/print@\", we probably want the --- contextPath to be \"@\/article@\" instead of \"@\/article\/whatever\/print@\". --- --- A path component within an URL entry point beginning with a colon (\"@:@\") --- is treated as a /variable capture/; the corresponding path component within --- the request URI will be entered into the 'rqParams' parameters mapping with --- the given name. For instance, if the routes were: --- --- > route [ ("foo/:bar/baz", fooBazHandler) ] --- --- Then a request for \"@\/foo\/saskatchewan\/baz@\" would be routed to --- @fooBazHandler@ with a mapping for: --- --- > "bar" => "saskatchewan" --- --- in its parameters table. --- --- Longer paths are matched first, and specific routes are matched before --- captures. That is, if given routes: --- --- > [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] --- --- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will go --- to @h3@, and \"@\/a@\" will go to @h1@. --- --- The following example matches \"@\/article@\" to an article index, --- \"@\/login@\" to a login, and \"@\/article\/...@\" to an article renderer. --- --- > route [ ("article", renderIndex) --- > , ("article/:id", renderArticle) --- > , ("login", method POST doLogin) ] --- -route :: [(ByteString, Snap a)] -> Snap a -route rts = route' (return ()) rts' [] - where - rts' = mconcat (map pRoute rts) - - ------------------------------------------------------------------------------- --- | The 'routeLocal' function is the same as 'route', except it doesn't change --- the request's context path. This is useful if you want to route to a --- particular handler but you want that handler to receive the 'rqPathInfo' as --- it is. -routeLocal :: [(ByteString, Snap a)] -> Snap a -routeLocal rts' = do - req <- getRequest - let ctx = rqContextPath req - let p = rqPathInfo req - let md = modifyRequest $ \r -> r {rqContextPath=ctx, rqPathInfo=p} - - route' md rts [] <|> (md >> pass) - - where - rts = mconcat (map pRoute rts') - - ------------------------------------------------------------------------------- -pRoute :: (ByteString, Snap a) -> Route a -pRoute (r, a) = foldr f (Action a) hier - where - hier = filter (not . B.null) $ B.splitWith (== (c2w '/')) r - f s rt = if B.head s == c2w ':' - then Capture (B.tail s) rt NoRoute - else Dir (Map.fromList [(s, rt)]) NoRoute - - ------------------------------------------------------------------------------- -route' :: Snap () -- ^ an action to be run before any user - -- handler - -> Route a -- ^ currently active routing table - -> [Route a] -- ^ list of fallback routing tables in case - -- the current table fails - -> Snap a -route' pre (Action action) _ = pre >> action - -route' pre (Capture param rt fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - if B.null cwd - then route' pre fb fbs - else do localRequest (updateContextPath (B.length cwd) . (f cwd)) $ - route' pre rt (fb:fbs) - where - f v req = req { rqParams = Map.insertWith (++) param [v] (rqParams req) } - -route' pre (Dir rtm fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - case Map.lookup cwd rtm of - Just rt -> do - localRequest (updateContextPath (B.length cwd)) $ - route' pre rt (fb:fbs) - Nothing -> route' pre fb fbs - -route' _ NoRoute [] = pass -route' pre NoRoute (fb:fbs) = route' pre fb fbs -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Internal-Types.html b/static/docs/0.1.1/snap-core/src/Snap-Internal-Types.html deleted file mode 100644 index 47a4af5..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Internal-Types.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Types where - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Exception (throwIO, ErrorCall(..)) -import Control.Monad.CatchIO -import Control.Monad.State.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.IORef -import qualified Data.Iteratee as Iter -import Data.Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Data.Text.Lazy as LT -import qualified Data.Text.Lazy.Encoding as LT - -import Data.Typeable - ------------------------------------------------------------------------------- -import Snap.Iteratee hiding (Enumerator) -import Snap.Internal.Http.Types - - ------------------------------------------------------------------------------- --- The Snap Monad ------------------------------------------------------------------------------- - -{-| - -'Snap' is the 'Monad' that user web handlers run in. 'Snap' gives you: - -1. stateful access to fetch or modify an HTTP 'Request' - -2. stateful access to fetch or modify an HTTP 'Response' - -3. failure \/ 'Alternative' \/ 'MonadPlus' semantics: a 'Snap' handler can - choose not to handle a given request, using 'empty' or its synonym 'pass', - and you can try alternative handlers with the '<|>' operator: - - > a :: Snap String - > a = pass - > - > b :: Snap String - > b = return "foo" - > - > c :: Snap String - > c = a <|> b -- try running a, if it fails then try b - -4. convenience functions ('writeBS', 'writeLBS', 'writeText', 'writeLazyText', - 'addToOutput') for writing output to the 'Response': - - > a :: (forall a . Enumerator a) -> Snap () - > a someEnumerator = do - > writeBS "I'm a strict bytestring" - > writeLBS "I'm a lazy bytestring" - > addToOutput someEnumerator - -5. early termination: if you call 'finishWith': - - > a :: Snap () - > a = do - > modifyResponse $ setResponseStatus 500 "Internal Server Error" - > writeBS "500 error" - > r <- getResponse - > finishWith r - - then any subsequent processing will be skipped and supplied 'Response' value - will be returned from 'runSnap' as-is. - -6. access to the 'IO' monad through a 'MonadIO' instance: - - > a :: Snap () - > a = liftIO fireTheMissiles --} - - ------------------------------------------------------------------------------- -newtype Snap a = Snap { - unSnap :: StateT SnapState (Iteratee IO) (Maybe (Either Response a)) -} - - ------------------------------------------------------------------------------- -data SnapState = SnapState - { _snapRequest :: Request - , _snapResponse :: Response } - - ------------------------------------------------------------------------------- -instance Monad Snap where - (Snap m) >>= f = - Snap $ do - eth <- m - maybe (return Nothing) - (either (return . Just . Left) - (unSnap . f)) - eth - - return = Snap . return . Just . Right - fail = const $ Snap $ return Nothing - - ------------------------------------------------------------------------------- -instance MonadIO Snap where - liftIO m = Snap $ liftM (Just . Right) $ liftIO m - - ------------------------------------------------------------------------------- -instance MonadCatchIO Snap where - catch (Snap m) handler = Snap $ do - x <- try m - case x of - (Left e) -> let (Snap z) = handler e in z - (Right y) -> return y - - block (Snap m) = Snap $ block m - unblock (Snap m) = Snap $ unblock m - - ------------------------------------------------------------------------------- -instance MonadPlus Snap where - mzero = Snap $ return Nothing - - a `mplus` b = - Snap $ do - mb <- unSnap a - if isJust mb then return mb else unSnap b - - ------------------------------------------------------------------------------- -instance Functor Snap where - fmap = liftM - - ------------------------------------------------------------------------------- -instance Applicative Snap where - pure = return - (<*>) = ap - - ------------------------------------------------------------------------------- -instance Alternative Snap where - empty = mzero - (<|>) = mplus - - ------------------------------------------------------------------------------- -liftIter :: Iteratee IO a -> Snap a -liftIter i = Snap (lift i >>= return . Just . Right) - - ------------------------------------------------------------------------------- --- | Sends the request body through an iteratee (data consumer) and --- returns the result. -runRequestBody :: Iteratee IO a -> Snap a -runRequestBody iter = do - req <- getRequest - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - - -- make sure the iteratee consumes all of the output - let iter' = iter >>= (\a -> Iter.skipToEof >> return a) - - -- run the iteratee - result <- liftIter $ Iter.joinIM $ enum iter' - - -- stuff a new dummy enumerator into the request, so you can only try to - -- read the request body from the socket once - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . Iter.joinI . Iter.take 0 ) - - return result - - ------------------------------------------------------------------------------- --- | Returns the request body as a bytestring. -getRequestBody :: Snap L.ByteString -getRequestBody = liftM fromWrap $ runRequestBody stream2stream -{-# INLINE getRequestBody #-} - - ------------------------------------------------------------------------------- --- | Detaches the request body's 'Enumerator' from the 'Request' and --- returns it. You would want to use this if you needed to send the --- HTTP request body (transformed or otherwise) through to the output --- in O(1) space. (Examples: transcoding, \"echo\", etc) --- --- Normally Snap is careful to ensure that the request body is fully --- consumed after your web handler runs; this function is marked --- \"unsafe\" because it breaks this guarantee and leaves the --- responsibility up to you. If you don't fully consume the --- 'Enumerator' you get here, the next HTTP request in the pipeline --- (if any) will misparse. Be careful with exception handlers. -unsafeDetachRequestBody :: Snap (Enumerator a) -unsafeDetachRequestBody = do - req <- getRequest - let ioref = rqBody req - senum <- liftIO $ readIORef ioref - let (SomeEnumerator enum) = senum - liftIO $ writeIORef ioref - (SomeEnumerator $ return . Iter.joinI . Iter.take 0) - return enum - - ------------------------------------------------------------------------------- --- | Short-circuits a 'Snap' monad action early, storing the given --- 'Response' value in its state. -finishWith :: Response -> Snap () -finishWith = Snap . return . Just . Left -{-# INLINE finishWith #-} - - ------------------------------------------------------------------------------- --- | Fails out of a 'Snap' monad action. This is used to indicate --- that you choose not to handle the given request within the given --- handler. -pass :: Snap a -pass = empty - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only if the request's HTTP method matches --- the given method. -method :: Method -> Snap a -> Snap a -method m action = do - req <- getRequest - unless (rqMethod req == m) pass - action -{-# INLINE method #-} - - ------------------------------------------------------------------------------- --- Appends n bytes of the path info to the context path with a --- trailing slash. -updateContextPath :: Int -> Request -> Request -updateContextPath n req | n > 0 = req { rqContextPath = ctx - , rqPathInfo = pinfo } - | otherwise = req - where - ctx' = S.take n (rqPathInfo req) - ctx = S.concat [rqContextPath req, ctx', "/"] - pinfo = S.drop (n+1) (rqPathInfo req) - - ------------------------------------------------------------------------------- --- Runs a 'Snap' monad action only if the 'rqPathInfo' matches the given --- predicate. -pathWith :: (ByteString -> ByteString -> Bool) - -> ByteString - -> Snap a - -> Snap a -pathWith c p action = do - req <- getRequest - unless (c p (rqPathInfo req)) pass - localRequest (updateContextPath $ S.length p) action - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when the 'rqPathInfo' of the request --- starts with the given path. For example, --- --- > dir "foo" handler --- --- Will fail if 'rqPathInfo' is not \"@\/foo@\" or \"@\/foo\/...@\", and will --- add @\"foo\/\"@ to the handler's local 'rqContextPath'. -dir :: ByteString -- ^ path component to match - -> Snap a -- ^ handler to run - -> Snap a -dir = pathWith f - where - f dr pinfo = dr == x - where - (x,_) = S.break (=='/') pinfo -{-# INLINE dir #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is exactly --- equal to the given string. If the path matches, locally sets 'rqContextPath' --- to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\", and runs the given --- handler. -path :: ByteString -- ^ path to match against - -> Snap a -- ^ handler to run - -> Snap a -path = pathWith (==) -{-# INLINE path #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when 'rqPathInfo' is empty. -ifTop :: Snap a -> Snap a -ifTop = path "" -{-# INLINE ifTop #-} - - ------------------------------------------------------------------------------- --- | Local Snap version of 'get'. -sget :: Snap SnapState -sget = Snap $ liftM (Just . Right) get -{-# INLINE sget #-} - - ------------------------------------------------------------------------------- --- | Local Snap monad version of 'modify'. -smodify :: (SnapState -> SnapState) -> Snap () -smodify f = Snap $ modify f >> return (Just $ Right ()) -{-# INLINE smodify #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Request' object out of the 'Snap' monad. -getRequest :: Snap Request -getRequest = liftM _snapRequest sget -{-# INLINE getRequest #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Response' object out of the 'Snap' monad. -getResponse :: Snap Response -getResponse = liftM _snapResponse sget -{-# INLINE getResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Response' object into the 'Snap' monad. -putResponse :: Response -> Snap () -putResponse r = smodify $ \ss -> ss { _snapResponse = r } -{-# INLINE putResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Request' object into the 'Snap' monad. -putRequest :: Request -> Snap () -putRequest r = smodify $ \ss -> ss { _snapRequest = r } -{-# INLINE putRequest #-} - - ------------------------------------------------------------------------------- --- | Modifies the 'Request' object stored in a 'Snap' monad. -modifyRequest :: (Request -> Request) -> Snap () -modifyRequest f = smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss } -{-# INLINE modifyRequest #-} - - ------------------------------------------------------------------------------- --- | Modifes the 'Response' object stored in a 'Snap' monad. -modifyResponse :: (Response -> Response) -> Snap () -modifyResponse f = smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss } -{-# INLINE modifyResponse #-} - - ------------------------------------------------------------------------------- --- | Adds the output from the given enumerator to the 'Response' --- stored in the 'Snap' monad state. -addToOutput :: (forall a . Enumerator a) -- ^ output to add - -> Snap () -addToOutput enum = modifyResponse $ modifyResponseBody (>. enum) - - ------------------------------------------------------------------------------- --- | Adds the given strict 'ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeBS :: ByteString -> Snap () -writeBS s = addToOutput $ enumBS s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeLBS :: L.ByteString -> Snap () -writeLBS s = addToOutput $ enumLBS s - - ------------------------------------------------------------------------------- --- | Adds the given strict 'T.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeText :: T.Text -> Snap () -writeText s = writeBS $ T.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'LT.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeLazyText :: LT.Text -> Snap () -writeLazyText s = writeLBS $ LT.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Sets the output to be the contents of the specified file. --- --- Calling 'sendFile' will overwrite any output queued to be sent in the --- 'Response'. If the response body is not modified after the call to --- 'sendFile', Snap will use the efficient @sendfile()@ system call on --- platforms that support it. --- --- If the response body is modified (using 'modifyResponseBody'), the file will --- be read using @mmap()@. -sendFile :: FilePath -> Snap () -sendFile f = modifyResponse $ \r -> r { rspBody = SendFile f } - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' action with a locally-modified 'Request' state --- object. The 'Request' object in the Snap monad state after the call --- to localRequest will be unchanged. -localRequest :: (Request -> Request) -> Snap a -> Snap a -localRequest f m = do - req <- getRequest - - runAct req <|> (putRequest req >> pass) - - where - runAct req = do - modifyRequest f - result <- m - putRequest req - return result -{-# INLINE localRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Request' from state and hands it to the given action. -withRequest :: (Request -> Snap a) -> Snap a -withRequest = (getRequest >>=) -{-# INLINE withRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Response' from state and hands it to the given action. -withResponse :: (Response -> Snap a) -> Snap a -withResponse = (getResponse >>=) -{-# INLINE withResponse #-} - - ------------------------------------------------------------------------------- --- | This exception is thrown if the handler you supply to 'runSnap' fails. -data NoHandlerException = NoHandlerException - deriving (Eq, Typeable) - - ------------------------------------------------------------------------------- -instance Show NoHandlerException where - show NoHandlerException = "No handler for request" - - ------------------------------------------------------------------------------- -instance Exception NoHandlerException - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action in the 'Iteratee IO' monad. -runSnap :: Snap a -> Request -> Iteratee IO (Request,Response) -runSnap (Snap m) req = do - (r, ss') <- runStateT m ss - - e <- maybe (return $ Left fourohfour) - return - r - - -- is this a case of early termination? - let resp = case e of - Left x -> x - Right _ -> _snapResponse ss' - - return (_snapRequest ss', resp) - - where - fourohfour = setContentLength 3 $ - setResponseStatus 404 "Not Found" $ - modifyResponseBody (>. enumBS "404") $ - emptyResponse - - dresp = emptyResponse { rspHttpVersion = rqVersion req } - - ss = SnapState req dresp -{-# INLINE runSnap #-} - - ------------------------------------------------------------------------------- -evalSnap :: Snap a -> Request -> Iteratee IO a -evalSnap (Snap m) req = do - (r, _) <- runStateT m ss - - e <- maybe (liftIO $ throwIO NoHandlerException) - return - r - - -- is this a case of early termination? - case e of - Left _ -> liftIO $ throwIO $ ErrorCall "no value" - Right x -> return x - where - dresp = emptyResponse { rspHttpVersion = rqVersion req } - ss = SnapState req dresp -{-# INLINE evalSnap #-} - - - ------------------------------------------------------------------------------- --- | See 'rqParam'. Looks up a value for the given named parameter in the --- 'Request'. If more than one value was entered for the given parameter name, --- 'getParam' gloms the values together with: --- --- @ 'S.intercalate' \" \"@ --- -getParam :: ByteString -- ^ parameter name to look up - -> Snap (Maybe ByteString) -getParam k = do - rq <- getRequest - return $ liftM (S.intercalate " ") $ rqParam k rq - - -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Iteratee.html b/static/docs/0.1.1/snap-core/src/Snap-Iteratee.html deleted file mode 100644 index 0aecc98..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Iteratee.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} - --- | Snap Framework type aliases and utilities for iteratees. Note that as a --- convenience, this module also exports everything from @Data.Iteratee@ in the --- @iteratee@ library. --- --- /WARNING/: Note that all of these types are scheduled to change in the --- @darcs@ head version of the @iteratee@ library; John Lato et al. are working --- on a much improved iteratee formulation. - -module Snap.Iteratee - ( -- * Convenience aliases around types from @Data.Iteratee@ - Stream - , IterV - , Iteratee - , Enumerator - - -- * Re-export types and functions from @Data.Iteratee@ - , module Data.Iteratee - - -- * Helper functions - - -- ** Enumerators - , enumBS - , enumLBS - , enumFile - - -- ** Conversion to/from 'WrappedByteString' - , fromWrap - , toWrap - - -- ** Iteratee utilities - , takeExactly - , takeNoMoreThan - , countBytes - , bufferIteratee - ) where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad -import Control.Monad.CatchIO -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Lazy as L -import Data.Iteratee -import qualified Data.Iteratee.Base.StreamChunk as SC -import Data.Iteratee.WrappedByteString -import Data.Monoid (mappend) -import Data.Word (Word8) -import Prelude hiding (catch,drop) -import System.IO.Posix.MMap -import qualified Data.DList as D ------------------------------------------------------------------------------- - -type Stream = StreamG WrappedByteString Word8 -type IterV m = IterGV WrappedByteString Word8 m -type Iteratee m = IterateeG WrappedByteString Word8 m -type Enumerator m a = Iteratee m a -> m (Iteratee m a) - - ------------------------------------------------------------------------------- -instance (Functor m, MonadCatchIO m) => - MonadCatchIO (IterateeG s el m) where - --catch :: Exception e => m a -> (e -> m a) -> m a - catch m handler = IterateeG $ \str -> do - ee <- try $ runIter m str - case ee of - (Left e) -> runIter (handler e) str - (Right v) -> return v - - --block :: m a -> m a - block m = IterateeG $ \str -> block $ runIter m str - unblock m = IterateeG $ \str -> unblock $ runIter m str - - ------------------------------------------------------------------------------- --- | Wraps an 'Iteratee', counting the number of bytes consumed by it. -countBytes :: (Monad m) => Iteratee m a -> Iteratee m (a, Int) -countBytes = go 0 - where - go !n iter = IterateeG $ f n iter - - f !n !iter ch@(Chunk ws) = do - iterv <- runIter iter ch - case iterv of - Done x rest -> let !n' = n + m - len rest - in return $! Done (x, n') rest - Cont i err -> return $ Cont ((go $! n + m) i) err - where - m = S.length $ unWrap ws - - len (EOF _) = 0 - len (Chunk s) = S.length $ unWrap s - - f !n !iter stream = do - iterv <- runIter iter stream - case iterv of - Done x rest -> return $ Done (x, n) rest - Cont i err -> return $ Cont (go n i) err - - ------------------------------------------------------------------------------- --- | Buffers an iteratee. --- --- Our enumerators produce a lot of little strings; rather than spending all --- our time doing kernel context switches for 4-byte write() calls, we buffer --- the iteratee to send 2KB at a time. -bufferIteratee :: (Monad m) => Enumerator m a -bufferIteratee = return . go (D.empty,0) - where - blocksize = 2048 - - --go :: (DList ByteString, Int) -> Iteratee m a -> Iteratee m a - go (!dl,!n) iter = IterateeG $! f (dl,n) iter - - --f :: (DList ByteString, Int) -> Iteratee m a -> Stream -> m (IterV m a) - f _ !iter ch@(EOF (Just _)) = runIter iter ch - f (!dl,_) !iter ch@(EOF Nothing) = do - iterv <- runIter iter $ Chunk big - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> runIter i ch - where - big = toWrap $ L.fromChunks [S.concat $ D.toList dl] - - f (!dl,!n) iter (Chunk ws) = - if n' > blocksize - then do - iterv <- runIter iter (Chunk big) - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> return $ Cont (go (D.empty,0) i) Nothing - else return $ Cont (go (dl',n') iter) Nothing - where - s = S.concat $ L.toChunks $ fromWrap ws - m = S.length s - n' = n+m - dl' = D.snoc dl s - big = toWrap $ L.fromChunks [S.concat $ D.toList dl'] - - ------------------------------------------------------------------------------- --- | Enumerates a strict bytestring. -enumBS :: (Monad m) => ByteString -> Enumerator m a -enumBS bs = enumPure1Chunk $ WrapBS bs -{-# INLINE enumBS #-} - - ------------------------------------------------------------------------------- --- | Enumerates a lazy bytestring. -enumLBS :: (Monad m) => L.ByteString -> Enumerator m a -enumLBS lbs iter = foldM k iter enums - where - enums = map (enumPure1Chunk . WrapBS) $ L.toChunks lbs - k i e = e i - - ------------------------------------------------------------------------------- --- | Converts a lazy bytestring to a wrapped bytestring. -toWrap :: L.ByteString -> WrappedByteString Word8 -toWrap = WrapBS . S.concat . L.toChunks -{-# INLINE toWrap #-} - - ------------------------------------------------------------------------------- --- | Converts a wrapped bytestring to a lazy bytestring. -fromWrap :: WrappedByteString Word8 -> L.ByteString -fromWrap = L.fromChunks . (:[]) . unWrap -{-# INLINE fromWrap #-} - - ------------------------------------------------------------------------------- --- | Reads n elements from a stream and applies the given iteratee to --- the stream of the read elements. Reads exactly n elements, and if --- the stream is short propagates an error. -takeExactly :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeExactly 0 iter = return iter -takeExactly n' iter = - if n' < 0 - then takeExactly 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeExactly n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - step n (Chunk str) = done (Chunk s1) (Chunk s2) - where (s1, s2) = SC.splitAt n str - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n (EOF Nothing) = return $ Cont undefined (Just (Err "short write")) - check n (Done x _) = drop n >> return (return x) - check n (Cont x Nothing) = takeExactly n x - check n (Cont _ (Just e)) = drop n >> throwErr e - done s1 s2 = liftM (flip Done s2) (runIter iter s1 >>= checkIfDone return) - - ------------------------------------------------------------------------------- --- | Reads up to n elements from a stream and applies the given iteratee to the --- stream of the read elements. If more than n elements are read, propagates an --- error. -takeNoMoreThan :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeNoMoreThan n' iter = - if n' < 0 - then takeNoMoreThan 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeNoMoreThan n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - | otherwise = done (Chunk s1) (Chunk s2) - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - (s1, s2) = SC.splitAt n str - - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n chk@(EOF Nothing) = do - v <- runIter iter chk - - case v of - (Done x s) -> return $ Done (return x) s - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont _ Nothing) -> return $ Cont (throwErr $ Err "premature EOF") Nothing - - check _ v@(Done _ _) = return $ liftI v - check n (Cont x Nothing) = takeNoMoreThan n x - check _ (Cont _ (Just e)) = throwErr e - - done _ (EOF _) = error "impossible" - done s1 s2@(Chunk s2') = do - v <- runIter iter s1 - case v of - (Done x s') -> return $ Done (return x) (s' `mappend` s2) - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont i Nothing) -> - if SC.null s2' - then return $ Cont (takeNoMoreThan 0 i) Nothing - else return $ Cont undefined (Just $ Err "too many bytes") - - ------------------------------------------------------------------------------- -enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a) -enumFile fp iter = do - es <- (try $ - liftM WrapBS $ - unsafeMMapFile fp) :: IO (Either SomeException (WrappedByteString Word8)) - - case es of - (Left e) -> return $ throwErr $ Err $ "IO error" ++ show e - (Right s) -> liftM liftI $ runIter iter $ Chunk s -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Types.html b/static/docs/0.1.1/snap-core/src/Snap-Types.html deleted file mode 100644 index 69d5c03..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Types.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - -
{-| - -This module contains the core type definitions, class instances, and functions -for HTTP as well as the 'Snap' monad, which is used for web handlers. - --} -module Snap.Types - ( - -- * The Snap Monad - Snap - , runSnap - , NoHandlerException(..) - - -- ** Functions for control flow and early termination - , finishWith - , pass - - -- ** Routing - , method - , path - , dir - , ifTop - , route - , routeLocal - - -- ** Access to state - , getRequest - , getResponse - , putRequest - , putResponse - , modifyRequest - , modifyResponse - , localRequest - , withRequest - , withResponse - - -- ** Grabbing request bodies - , runRequestBody - , getRequestBody - , unsafeDetachRequestBody - -- * HTTP Datatypes and Functions - -- $httpDoc - -- - , Request - , Response - , Headers - , HasHeaders(..) - , Params - , Method(..) - , Cookie(..) - , HttpVersion - - -- ** Headers - , addHeader - , setHeader - , getHeader - - -- ** Requests - , rqServerName - , rqServerPort - , rqRemoteAddr - , rqRemotePort - , rqLocalAddr - , rqLocalHostname - , rqIsSecure - , rqContentLength - , rqMethod - , rqVersion - , rqCookies - , rqPathInfo - , rqContextPath - , rqURI - , rqQueryString - , rqParams - , rqParam - , getParam - , rqModifyParams - , rqSetParam - - -- ** Responses - , emptyResponse - , setResponseStatus - , rspStatus - , rspStatusReason - , setContentType - , addCookie - , setContentLength - , clearContentLength - - -- *** Response I/O - , setResponseBody - , modifyResponseBody - , addToOutput - , writeBS - , writeLazyText - , writeText - , writeLBS - , sendFile - - -- * Iteratee - , Enumerator - - -- * HTTP utilities - , formatHttpTime - , parseHttpTime - , urlEncode - , urlDecode - ) where - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Routing -import Snap.Internal.Types ------------------------------------------------------------------------------- - --- $httpDoc --- HTTP-related datatypes: 'Request', 'Response', 'Cookie', etc. -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Util-FileServe.html b/static/docs/0.1.1/snap-core/src/Snap-Util-FileServe.html deleted file mode 100644 index 38c9a3f..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Util-FileServe.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | Contains web handlers to serve files from a directory. -module Snap.Util.FileServe -( - getSafePath -, fileServe -, fileServe' -, fileServeSingle -, fileServeSingle' -, defaultMimeTypes -, MimeMap -) where - ------------------------------------------------------------------------------- -import Control.Monad -import Control.Monad.Trans -import qualified Data.ByteString.Char8 as S -import Data.ByteString.Char8 (ByteString) -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import System.Directory -import System.FilePath -import System.Posix.Files - ------------------------------------------------------------------------------- -import Snap.Types - - ------------------------------------------------------------------------------- --- | A type alias for MIME type -type MimeMap = Map FilePath ByteString - - ------------------------------------------------------------------------------- --- | The default set of mime type mappings we use when serving files. Its --- value: --- --- > Map.fromList [ --- > ( ".asc" , "text/plain" ), --- > ( ".asf" , "video/x-ms-asf" ), --- > ( ".asx" , "video/x-ms-asf" ), --- > ( ".avi" , "video/x-msvideo" ), --- > ( ".bz2" , "application/x-bzip" ), --- > ( ".c" , "text/plain" ), --- > ( ".class" , "application/octet-stream" ), --- > ( ".conf" , "text/plain" ), --- > ( ".cpp" , "text/plain" ), --- > ( ".css" , "text/css" ), --- > ( ".cxx" , "text/plain" ), --- > ( ".dtd" , "text/xml" ), --- > ( ".dvi" , "application/x-dvi" ), --- > ( ".gif" , "image/gif" ), --- > ( ".gz" , "application/x-gzip" ), --- > ( ".hs" , "text/plain" ), --- > ( ".htm" , "text/html" ), --- > ( ".html" , "text/html" ), --- > ( ".jar" , "application/x-java-archive" ), --- > ( ".jpeg" , "image/jpeg" ), --- > ( ".jpg" , "image/jpeg" ), --- > ( ".js" , "text/javascript" ), --- > ( ".log" , "text/plain" ), --- > ( ".m3u" , "audio/x-mpegurl" ), --- > ( ".mov" , "video/quicktime" ), --- > ( ".mp3" , "audio/mpeg" ), --- > ( ".mpeg" , "video/mpeg" ), --- > ( ".mpg" , "video/mpeg" ), --- > ( ".ogg" , "application/ogg" ), --- > ( ".pac" , "application/x-ns-proxy-autoconfig" ), --- > ( ".pdf" , "application/pdf" ), --- > ( ".png" , "image/png" ), --- > ( ".ps" , "application/postscript" ), --- > ( ".qt" , "video/quicktime" ), --- > ( ".sig" , "application/pgp-signature" ), --- > ( ".spl" , "application/futuresplash" ), --- > ( ".swf" , "application/x-shockwave-flash" ), --- > ( ".tar" , "application/x-tar" ), --- > ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), --- > ( ".tar.gz" , "application/x-tgz" ), --- > ( ".tbz" , "application/x-bzip-compressed-tar" ), --- > ( ".text" , "text/plain" ), --- > ( ".tgz" , "application/x-tgz" ), --- > ( ".torrent" , "application/x-bittorrent" ), --- > ( ".txt" , "text/plain" ), --- > ( ".wav" , "audio/x-wav" ), --- > ( ".wax" , "audio/x-ms-wax" ), --- > ( ".wma" , "audio/x-ms-wma" ), --- > ( ".wmv" , "video/x-ms-wmv" ), --- > ( ".xbm" , "image/x-xbitmap" ), --- > ( ".xml" , "text/xml" ), --- > ( ".xpm" , "image/x-xpixmap" ), --- > ( ".xwd" , "image/x-xwindowdump" ), --- > ( ".zip" , "application/zip" ) ] --- -defaultMimeTypes :: MimeMap -defaultMimeTypes = Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".ttf" , "application/x-font-truetype" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - ------------------------------------------------------------------------------- --- | Gets a path from the 'Request' using 'rqPathInfo' and makes sure it is --- safe to use for opening files. A path is safe if it is a relative path --- and has no ".." elements to escape the intended directory structure. -getSafePath :: Snap FilePath -getSafePath = do - req <- getRequest - let p = S.unpack $ rqPathInfo req - - -- check that we don't have any sneaky .. paths - let dirs = splitDirectories p - when (elem ".." dirs) pass - return p - - ------------------------------------------------------------------------------- --- | Serves files out of the given directory. The relative path given in --- 'rqPathInfo' is searched for the given file, and the file is served with the --- appropriate mime type if it is found. Absolute paths and \"@..@\" are prohibited --- to prevent files from being served from outside the sandbox. --- --- Uses 'defaultMimeTypes' to determine the @Content-Type@ based on the file's --- extension. -fileServe :: FilePath -- ^ root directory - -> Snap () -fileServe = fileServe' defaultMimeTypes -{-# INLINE fileServe #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServe', with control over the MIME mapping used. -fileServe' :: MimeMap -- ^ MIME type mapping - -> FilePath -- ^ root directory - -> Snap () -fileServe' mm root = do - sp <- getSafePath - let fp = root </> sp - - -- check that the file exists - liftIO (doesFileExist fp) >>= flip unless pass - - let fn = takeFileName fp - let mime = fileType mm fn - fileServeSingle' mime fp -{-# INLINE fileServe' #-} - - ------------------------------------------------------------------------------- --- | Serves a single file specified by a full or relative path. The --- path restrictions on fileServe don't apply to this function since --- the path is not being supplied by the user. -fileServeSingle :: FilePath -- ^ path to file - -> Snap () -fileServeSingle fp = - fileServeSingle' (fileType defaultMimeTypes (takeFileName fp)) fp -{-# INLINE fileServeSingle #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServeSingle', with control over the MIME mapping used. -fileServeSingle' :: ByteString -- ^ MIME type mapping - -> FilePath -- ^ path to file - -> Snap () -fileServeSingle' mime fp = do - req <- getRequest - - let mbH = getHeader "if-modified-since" req - mbIfModified <- liftIO $ case mbH of - Nothing -> return Nothing - (Just s) -> liftM Just $ parseHttpTime s - - -- check modification time and bug out early if the file is not modified. - filestat <- liftIO $ getFileStatus fp - let mt = modificationTime filestat - maybe (return ()) (chkModificationTime mt) mbIfModified - - let sz = fromEnum $ fileSize filestat - lm <- liftIO $ formatHttpTime mt - - modifyResponse $ setHeader "Last-Modified" lm - . setContentType mime - . setContentLength sz - sendFile fp - - where - -------------------------------------------------------------------------- - chkModificationTime mt lt = when (mt <= lt) notModified - - -------------------------------------------------------------------------- - notModified = finishWith $ - setResponseStatus 304 "Not Modified" emptyResponse - - ------------------------------------------------------------------------------- -fileType :: MimeMap -> FilePath -> ByteString -fileType mm f = - if null ext - then defaultMimeType - else fromMaybe (fileType mm (drop 1 ext)) - mbe - - where - ext = takeExtensions f - mbe = Map.lookup ext mm - - ------------------------------------------------------------------------------- -defaultMimeType :: ByteString -defaultMimeType = "application/octet-stream" -- diff --git a/static/docs/0.1.1/snap-core/src/Snap-Util-GZip.html b/static/docs/0.1.1/snap-core/src/Snap-Util-GZip.html deleted file mode 100644 index 578c30d..0000000 --- a/static/docs/0.1.1/snap-core/src/Snap-Util-GZip.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Util.GZip -( withCompression -, withCompression' ) where - -import qualified Codec.Compression.GZip as GZip -import qualified Codec.Compression.Zlib as Zlib -import Control.Concurrent -import Control.Applicative hiding (many) -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.Attoparsec.Char8 hiding (Done) -import qualified Data.Attoparsec.Char8 as Atto -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Char8 (ByteString) -import Data.Iteratee.WrappedByteString -import Data.Maybe -import qualified Data.Set as Set -import Data.Set (Set) -import Data.Typeable -import Prelude hiding (catch, takeWhile) - ------------------------------------------------------------------------------- -import Snap.Internal.Debug -import Snap.Iteratee hiding (Enumerator) -import Snap.Types - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' web handler with compression if available. --- --- If the client has indicated support for @gzip@ or @compress@ in its --- @Accept-Encoding@ header, and the @Content-Type@ in the response is one of --- the following types: --- --- * @application/x-javascript@ --- --- * @text/css@ --- --- * @text/html@ --- --- * @text/javascript@ --- --- * @text/plain@ --- --- * @text/xml@ --- --- * @application/x-font-truetype@ --- --- Then the given handler's output stream will be compressed, --- @Content-Encoding@ will be set in the output headers, and the --- @Content-Length@ will be cleared if it was set. (We can't process the stream --- in O(1) space if the length is known beforehand.) --- --- The wrapped handler will be run to completion, and then the 'Response' --- that's contained within the 'Snap' monad state will be passed to --- 'finishWith' to prevent further processing. --- -withCompression :: Snap a -- ^ the web handler to run - -> Snap () -withCompression = withCompression' compressibleMimeTypes - - ------------------------------------------------------------------------------- --- | The same as 'withCompression', with control over which MIME types to --- compress. -withCompression' :: Set ByteString - -- ^ set of compressible MIME types - -> Snap a - -- ^ the web handler to run - -> Snap () -withCompression' mimeTable action = do - _ <- action - resp <- getResponse - - -- If a content-encoding is already set, do nothing. This prevents - -- "withCompression $ withCompression m" from ruining your day. - if isJust $ getHeader "Content-Encoding" resp - then return () - else do - let mbCt = getHeader "Content-Type" resp - - debug $ "withCompression', content-type is " ++ show mbCt - - case mbCt of - (Just ct) -> if Set.member ct mimeTable - then chkAcceptEncoding - else return () - _ -> return () - - - getResponse >>= finishWith - - where - chkAcceptEncoding :: Snap () - chkAcceptEncoding = do - req <- getRequest - debug $ "checking accept-encoding" - let mbAcc = getHeader "Accept-Encoding" req - debug $ "accept-encoding is " ++ show mbAcc - let s = fromMaybe "" mbAcc - - types <- liftIO $ parseAcceptEncoding s - - chooseType types - - - chooseType [] = return () - chooseType ("gzip":_) = gzipCompression - chooseType ("compress":_) = compressCompression - chooseType ("x-gzip":_) = gzipCompression - chooseType ("x-compress":_) = compressCompression - chooseType (_:xs) = chooseType xs - - ------------------------------------------------------------------------------- --- private following ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -compressibleMimeTypes :: Set ByteString -compressibleMimeTypes = Set.fromList [ "application/x-font-truetype" - , "application/x-javascript" - , "text/css" - , "text/html" - , "text/javascript" - , "text/plain" - , "text/xml" ] - - - - ------------------------------------------------------------------------------- -gzipCompression :: Snap () -gzipCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "gzip" . - clearContentLength . - modifyResponseBody gcompress - - ------------------------------------------------------------------------------- -compressCompression :: Snap () -compressCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "compress" . - clearContentLength . - modifyResponseBody ccompress - - ------------------------------------------------------------------------------- -gcompress :: forall a . Enumerator a -> Enumerator a -gcompress = compressEnumerator GZip.compress - - ------------------------------------------------------------------------------- -ccompress :: forall a . Enumerator a -> Enumerator a -ccompress = compressEnumerator Zlib.compress - - ------------------------------------------------------------------------------- -compressEnumerator :: forall a . - (L.ByteString -> L.ByteString) - -> Enumerator a - -> Enumerator a -compressEnumerator compFunc enum iteratee = do - writeEnd <- newChan - readEnd <- newChan - tid <- forkIO $ threadProc readEnd writeEnd - - enum (IterateeG $ f readEnd writeEnd tid iteratee) - - where - -------------------------------------------------------------------------- - streamFinished :: Stream -> Bool - streamFinished (EOF _) = True - streamFinished (Chunk _) = False - - - -------------------------------------------------------------------------- - consumeSomeOutput :: Chan Stream - -> Iteratee IO a - -> IO (Iteratee IO a) - consumeSomeOutput writeEnd iter = do - e <- isEmptyChan writeEnd - if e - then return iter - else do - ch <- readChan writeEnd - - iter' <- liftM liftI $ runIter iter ch - if (streamFinished ch) - then return iter' - else consumeSomeOutput writeEnd iter' - - - -------------------------------------------------------------------------- - consumeRest :: Chan Stream - -> Iteratee IO a - -> IO (IterV IO a) - consumeRest writeEnd iter = do - ch <- readChan writeEnd - - iv <- runIter iter ch - if (streamFinished ch) - then return iv - else consumeRest writeEnd $ liftI iv - - - -------------------------------------------------------------------------- - f readEnd writeEnd tid i (EOF Nothing) = do - writeChan readEnd Nothing - x <- consumeRest writeEnd i - killThread tid - return x - - f _ _ tid i ch@(EOF (Just _)) = do - x <- runIter i ch - killThread tid - return x - - f readEnd writeEnd tid i (Chunk s') = do - let s = unWrap s' - writeChan readEnd $ Just s - i' <- consumeSomeOutput writeEnd i - return $ Cont (IterateeG $ f readEnd writeEnd tid i') Nothing - - - -------------------------------------------------------------------------- - threadProc :: Chan (Maybe ByteString) - -> Chan Stream - -> IO () - threadProc readEnd writeEnd = do - stream <- getChanContents readEnd - let bs = L.fromChunks $ streamToChunks stream - - let output = L.toChunks $ compFunc bs - let runIt = do - mapM_ (writeChan writeEnd . toChunk) output - writeChan writeEnd $ EOF Nothing - - runIt `catch` \(e::SomeException) -> - writeChan writeEnd $ EOF (Just $ Err $ show e) - - - -------------------------------------------------------------------------- - streamToChunks [] = [] - streamToChunks (Nothing:_) = [] - streamToChunks ((Just x):xs) = x:(streamToChunks xs) - - - -------------------------------------------------------------------------- - toChunk = Chunk . WrapBS - - ------------------------------------------------------------------------------- -fullyParse :: ByteString -> Parser a -> Either String a -fullyParse s p = - case r' of - (Fail _ _ e) -> Left e - (Partial _) -> Left "parse failed" - (Atto.Done _ x) -> Right x - where - r = parse p s - r' = feed r "" - - ------------------------------------------------------------------------------- --- We're not gonna bother with quality values; we'll do gzip or compress in --- that order. -acceptParser :: Parser [ByteString] -acceptParser = do - xs <- option [] $ (:[]) <$> encoding - ys <- many (char ',' *> encoding) - endOfInput - return $ xs ++ ys - where - encoding = skipSpace *> c <* skipSpace - - c = do - x <- coding - option () qvalue - return x - - qvalue = do - skipSpace - char ';' - skipSpace - char 'q' - skipSpace - char '=' - float - return () - - coding = string "*" <|> takeWhile isAlpha_ascii - - float = takeWhile isDigit >> - option () (char '.' >> takeWhile isDigit >> pure ()) - - ------------------------------------------------------------------------------- -data BadAcceptEncodingException = BadAcceptEncodingException - deriving (Typeable) - - ------------------------------------------------------------------------------- -instance Show BadAcceptEncodingException where - show BadAcceptEncodingException = "bad 'accept-encoding' header" - - ------------------------------------------------------------------------------- -instance Exception BadAcceptEncodingException - - ------------------------------------------------------------------------------- -parseAcceptEncoding :: ByteString -> IO [ByteString] -parseAcceptEncoding s = - case r of - Left _ -> throwIO BadAcceptEncodingException - Right x -> return x - where - r = fullyParse s acceptParser - -- diff --git a/static/docs/0.1.1/snap-core/src/hscolour.css b/static/docs/0.1.1/snap-core/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.1/snap-core/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.1.1/snap-server/Snap-Http-Server-Config.html b/static/docs/0.1.1/snap-server/Snap-Http-Server-Config.html deleted file mode 100644 index 52bc6c1..0000000 --- a/static/docs/0.1.1/snap-server/Snap-Http-Server-Config.html +++ /dev/null @@ -1,287 +0,0 @@ - - -
| ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||
| ||||||||||||||||
Description | ||||||||||||||||
The Snap HTTP server is a high performance, epoll-enabled, iteratee-based - web server library written in Haskell. Together with the snap-core library - upon which it depends, it provides a clean and efficient Haskell programming - interface to the HTTP protocol. - | ||||||||||||||||
Synopsis | ||||||||||||||||
| ||||||||||||||||
Documentation | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||
| ||||||||||||||||||
Synopsis | ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Prepares a log message with the time prepended. - | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Creates a new logger, logging to the given file. If the file argument is - "-", then log to stdout; if it's "stderr" then we log to stderr, - otherwise we log to a regular file in append mode. The file is closed and - re-opened every 15 minutes to facilitate external log rotation. - | ||||||||||||||||||
| ||||||||||||||||||
Sends out a log message verbatim with a newline appended. Note: - if you want a fancy log message you'll have to format it yourself - (or use combinedLogEntry). - | ||||||||||||||||||
| ||||||||||||||||||
Kills a logger thread, causing any unwritten contents to be - flushed out to disk - | ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||
snap-server-0.1.1: A fast, iteratee-based, epoll-enabled web server for the Snap Framework | ||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - The Snap HTTP server is a high performance, epoll-enabled, iteratee-based web -server library written in Haskell. Together with the snap-core library upon -which it depends, it provides a clean and efficient Haskell programming -interface to the HTTP protocol. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Snap.Http.Server.Config - ( Config(..) - , readConfigFromCmdLineArgs - ) where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import Data.ByteString.Internal (c2w) -import Data.ByteString.Char8 () -import Data.Maybe -import Data.Monoid -import System.Console.GetOpt -import System.Environment -import System.Exit -import System.IO - -data Config = Config - { localHostname :: !ByteString - , bindAddress :: !ByteString - , listenPort :: !Int - , accessLog :: !(Maybe FilePath) - , errorLog :: !(Maybe FilePath) - } deriving (Show) - - -data Flag = Flag - { flagLocalHost :: Maybe String - , flagBindAddress :: Maybe String - , flagPort :: Maybe Int - , flagAccessLog :: Maybe String - , flagErrorLog :: Maybe String - , flagUsage :: Bool - } - -instance Monoid Flag where - mempty = Flag Nothing Nothing Nothing Nothing Nothing False - - (Flag a1 b1 c1 d1 e1 f1) `mappend` (Flag a2 b2 c2 d2 e2 f2) = - Flag (getLast $ Last a1 `mappend` Last a2) - (getLast $ Last b1 `mappend` Last b2) - (getLast $ Last c1 `mappend` Last c2) - (getLast $ Last d1 `mappend` Last d2) - (getLast $ Last e1 `mappend` Last e2) - (f1 || f2) - -flagLH :: String -> Flag -flagLH s = mempty { flagLocalHost = Just s } - -flagBA :: String -> Flag -flagBA s = mempty { flagBindAddress = Just s } - -flagPt :: String -> Flag -flagPt p = mempty { flagPort = Just (read p) } - -flagAL :: String -> Flag -flagAL s = mempty { flagAccessLog = Just s } - -flagEL :: String -> Flag -flagEL s = mempty { flagErrorLog = Just s } - -flagHelp :: Flag -flagHelp = mempty { flagUsage = True } - -fromStr :: String -> ByteString -fromStr = B.pack . map c2w - -flags2config :: Flag -> Config -flags2config (Flag a b c d e _) = - Config (maybe "localhost" fromStr a) - (maybe "*" fromStr b) - (fromMaybe 8888 c) - d - e - - -options :: [OptDescr Flag] -options = - [ Option "l" ["localHostname"] - (ReqArg flagLH "STR") - "local hostname, default 'localhost'" - , Option "p" ["listenPort"] - (ReqArg flagPt "NUM") - "port to listen on, default 8888" - , Option "b" ["bindAddress"] - (ReqArg flagBA "STR") - "address to bind to, default '*'" - , Option "a" ["accessLog"] - (ReqArg flagAL "STR") - "access log in the 'combined' format, optional" - , Option "e" ["errorLog"] - (ReqArg flagEL "STR") - "error log, optional" - , Option "h" ["help"] - (NoArg flagHelp) - "display this usage statement" ] - - -readConfigFromCmdLineArgs :: String -- ^ application description, e.g. - -- \"Foo applet v0.2\" - -> IO Config -readConfigFromCmdLineArgs appName = do - argv <- getArgs - progName <- getProgName - - case getOpt Permute options argv of - (f,_,[] ) -> withFlags progName f - (_,_,errs) -> bombout progName errs - where - bombout progName errs = do - let hdr = appName ++ "\n\nUsage: " ++ progName ++ " [OPTIONS]" - let msg = concat errs ++ usageInfo hdr options - hPutStrLn stderr msg - exitFailure - - withFlags progName fs = do - let f = mconcat fs - if flagUsage f - then bombout progName [] - else return $ flags2config f -- diff --git a/static/docs/0.1.1/snap-server/src/Snap-Http-Server.html b/static/docs/0.1.1/snap-server/src/Snap-Http-Server.html deleted file mode 100644 index dbe6ebb..0000000 --- a/static/docs/0.1.1/snap-server/src/Snap-Http-Server.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -
-- | The Snap HTTP server is a high performance, epoll-enabled, iteratee-based --- web server library written in Haskell. Together with the @snap-core@ library --- upon which it depends, it provides a clean and efficient Haskell programming --- interface to the HTTP protocol. -module Snap.Http.Server -( - httpServe -) where - -import Data.ByteString (ByteString) -import Snap.Types -import qualified Snap.Internal.Http.Server as Int - - --- | Starts serving HTTP requests on the given port using the given handler. --- This function never returns; to shut down the HTTP server, kill the --- controlling thread. -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the (optional) access log - -> Maybe FilePath -- ^ path to the (optional) error log - -> Snap () -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alog elog handler = - Int.httpServe bindAddress bindPort localHostname alog elog handler' - where - handler' = runSnap handler -- diff --git a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Parser.html b/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Parser.html deleted file mode 100644 index a50a1de..0000000 --- a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Parser.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Http.Parser - ( IRequest(..) - , parseRequest - , readChunkedTransferEncoding - , parserToIteratee - , parseCookie - , parseUrlEncoded - , writeChunkedTransferEncoding - , strictize - ) where - - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Arrow (first, second) -import Control.Monad (liftM) -import Control.Monad.Trans -import Data.Attoparsec hiding (many, Result(..)) -import Data.Attoparsec.Iteratee -import Data.Bits -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Lazy as L -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import Data.Char -import Data.CIByteString -import Data.List (foldl') -import Data.Int -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (catMaybes, fromMaybe) -import Data.Time.Format (parseTime) -import qualified Data.Vector.Unboxed as Vec -import Data.Vector.Unboxed (Vector) -import Data.Word (Word8, Word64) -import Prelude hiding (take, takeWhile) -import System.Locale (defaultTimeLocale) ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Iteratee hiding (take, foldl') - - - ------------------------------------------------------------------------------- --- | an internal version of the headers part of an HTTP request -data IRequest = IRequest - { iMethod :: Method - , iRequestUri :: ByteString - , iHttpVersion :: (Int,Int) - , iRequestHeaders :: [(ByteString, ByteString)] - } - -instance Show IRequest where - show (IRequest m u v r) = - concat [ show m - , " " - , show u - , " " - , show v - , " " - , show r ] - ------------------------------------------------------------------------------- -parseRequest :: (Monad m) => Iteratee m (Maybe IRequest) -parseRequest = parserToIteratee pRequest - - -readChunkedTransferEncoding :: (Monad m) => Enumerator m a -readChunkedTransferEncoding iter = do - i <- chunkParserToEnumerator (parserToIteratee pGetTransferChunk) - iter - - return i - - -toHex :: Int64 -> ByteString -toHex !i' = S.reverse s - where - !i = abs i' - (!s,_) = S.unfoldrN 16 f (fromIntegral i) - - f :: Word64 -> Maybe (Word8, Word64) - f d = if d == 0 - then Nothing - else Just (ch, theRest) - - where - low4 = fromIntegral $ d .&. 0xf - ch = if low4 >= 10 - then c2w 'a' + low4 - 10 - else c2w '0' + low4 - theRest = (d .&. (complement 0xf)) `shiftR` 4 - - --- | Given an iteratee, produces a new one that wraps chunks sent to it with a --- chunked transfer-encoding. Example usage: --- --- > > (writeChunkedTransferEncoding --- > (enumLBS (L.fromChunks ["foo","bar","quux"])) --- > stream2stream) >>= --- > run >>= --- > return . fromWrap --- > --- > Chunk "3\r\nfoo\r\n3\r\nbar\r\n4\r\nquux\r\n0\r\n\r\n" Empty --- -writeChunkedTransferEncoding :: (Monad m) => Enumerator m a -> Enumerator m a -writeChunkedTransferEncoding enum it = do - i <- wrap it - enum i - - where - wrap iter = return $ IterateeG $ \s -> - case s of - (EOF Nothing) -> do - v <- runIter iter (Chunk $ toWrap "0\r\n\r\n") - i <- checkIfDone return v - runIter i (EOF Nothing) - (EOF e) -> return $ Cont undefined e - (Chunk x') -> do - let x = S.concat $ L.toChunks $ fromWrap x' - let n = S.length x - let o = L.fromChunks [ toHex (toEnum n) - , "\r\n" - , x - , "\r\n" ] - v <- runIter iter (Chunk $ toWrap o) - i <- checkIfDone wrap v - return $ Cont i Nothing - - -chunkParserToEnumerator :: (Monad m) => - Iteratee m (Maybe ByteString) - -> Iteratee m a - -> m (Iteratee m a) -chunkParserToEnumerator getChunk client = return $ do - mbB <- getChunk - maybe (finishIt client) (sendBS client) mbB - - where - sendBS iter s = do - v <- lift $ runIter iter (Chunk $ toWrap $ L.fromChunks [s]) - - case v of - (Done _ (EOF (Just e))) -> throwErr e - - (Done x _) -> return x - - (Cont _ (Just e)) -> throwErr e - - (Cont k Nothing) -> joinIM $ - chunkParserToEnumerator getChunk k - - finishIt iter = do - e <- lift $ sendEof iter - - case e of - Left x -> throwErr x - Right x -> return x - - sendEof iter = do - v <- runIter iter (EOF Nothing) - - return $ case v of - (Done _ (EOF (Just e))) -> Left e - (Done x _) -> Right x - (Cont _ (Just e)) -> Left e - (Cont _ _) -> Left $ Err $ "divergent iteratee" - - ------------------------------------------------------------------------------- --- parse functions ------------------------------------------------------------------------------- - --- theft alert: many of these routines adapted from Johan Tibell's hyena --- package - --- | Parsers for different tokens in an HTTP request. -sp, digit, letter :: Parser Word8 -sp = word8 $ c2w ' ' -digit = satisfy (isDigit . w2c) -letter = satisfy (isAlpha . w2c) - -untilEOL :: Parser ByteString -untilEOL = takeWhile notend - where - notend d = let c = w2c d in not $ c == '\r' || c == '\n' - -crlf :: Parser ByteString -crlf = string "\r\n" - --- | Parser for zero or more spaces. -spaces :: Parser [Word8] -spaces = many sp - -pSpaces :: Parser ByteString -pSpaces = takeWhile (isSpace . w2c) - --- | Parser for the internal request data type. -pRequest :: Parser (Maybe IRequest) -pRequest = (Just <$> pRequest') <|> (endOfInput *> pure Nothing) - -pRequest' :: Parser IRequest -pRequest' = IRequest - <$> (option "" crlf *> pMethod) <* sp - <*> pUri <* sp - <*> pVersion <* crlf - <*> pHeaders <* crlf - - -- note: the optional crlf is at the beginning because some older browsers - -- send an extra crlf after a POST body - - --- | Parser for the request method. -pMethod :: Parser Method -pMethod = (OPTIONS <$ string "OPTIONS") - <|> (GET <$ string "GET") - <|> (HEAD <$ string "HEAD") - <|> word8 (c2w 'P') *> ((POST <$ string "OST") <|> - (PUT <$ string "UT")) - <|> (DELETE <$ string "DELETE") - <|> (TRACE <$ string "TRACE") - <|> (CONNECT <$ string "CONNECT") - --- | Parser for the request URI. -pUri :: Parser ByteString -pUri = takeWhile (not . isSpace . w2c) - --- | Parser for the request's HTTP protocol version. -pVersion :: Parser (Int, Int) -pVersion = string "HTTP/" *> - liftA2 (,) (digit' <* word8 (c2w '.')) digit' - where - digit' = fmap (digitToInt . w2c) digit - -fieldChars :: Parser ByteString -fieldChars = takeWhile isFieldChar - where - isFieldChar c = (Vec.!) fieldCharTable (fromEnum c) - -fieldCharTable :: Vector Bool -fieldCharTable = Vec.generate 256 f - where - f d = let c=toEnum d in (isDigit c) || (isAlpha c) || c == '-' || c == '_' - - --- | Parser for request headers. -pHeaders :: Parser [(ByteString, ByteString)] -pHeaders = many header - where - header = {-# SCC "pHeaders/header" #-} - liftA2 (,) - fieldName - (word8 (c2w ':') *> spaces *> contents) - - fieldName = {-# SCC "pHeaders/fieldName" #-} - liftA2 S.cons letter fieldChars - - contents = {-# SCC "pHeaders/contents" #-} - liftA2 S.append - (untilEOL <* crlf) - (continuation <|> pure S.empty) - - isLeadingWS w = {-# SCC "pHeaders/isLeadingWS" #-} - elem w wstab - - wstab = map c2w " \t" - - leadingWhiteSpace = {-# SCC "pHeaders/leadingWhiteSpace" #-} - takeWhile1 isLeadingWS - - continuation = {-# SCC "pHeaders/continuation" #-} - liftA2 S.cons - (leadingWhiteSpace *> pure (c2w ' ')) - contents - - -pGetTransferChunk :: Parser (Maybe ByteString) -pGetTransferChunk = do - !hex <- liftM fromHex $ (takeWhile (isHexDigit . w2c)) - takeTill ((== '\r') . w2c) - crlf - if hex <= 0 - then return Nothing - else do - x <- take hex - crlf - return $ Just x - where - fromHex :: ByteString -> Int - fromHex s = Cvt.hex (L.fromChunks [s]) - - ------------------------------------------------------------------------------- --- COOKIE PARSING ------------------------------------------------------------------------------- - --- these definitions try to mirror RFC-2068 (the HTTP/1.1 spec) and RFC-2109 --- (cookie spec): please point out any errors! - -{-# INLINE matchAll #-} -matchAll :: [ Char -> Bool ] -> Char -> Bool -matchAll x c = and $ map ($ c) x - -{-# INLINE isToken #-} -isToken :: Char -> Bool -isToken c = (Vec.!) tokenTable (fromEnum c) - where - tokenTable :: Vector Bool - tokenTable = Vec.generate 256 (f . toEnum) - - f = matchAll [ isAscii - , not . isControl - , not . isSpace - , not . flip elem [ '(', ')', '<', '>', '@', ',', ';' - , ':', '\\', '\"', '/', '[', ']' - , '?', '=', '{', '}' ] - ] - -{-# INLINE isRFCText #-} -isRFCText :: Char -> Bool -isRFCText = not . isControl - -pToken :: Parser ByteString -pToken = takeWhile (isToken . w2c) - - -pQuotedString :: Parser ByteString -pQuotedString = q *> quotedText <* q - where - quotedText = (S.concat . reverse) <$> f [] - - f soFar = do - t <- takeWhile qdtext - - let soFar' = t:soFar - - -- RFC says that backslash only escapes for <"> - choice [ string "\\\"" *> f ("\"" : soFar') - , pure soFar' ] - - - q = word8 $ c2w '\"' - - qdtext = matchAll [ isRFCText, (/= '\"'), (/= '\\') ] . w2c - - -pCookie :: Parser Cookie -pCookie = do - -- grab kvps and turn to strict bytestrings - kvps <- pAvPairs - - -- kvps guaranteed non-null due to grammar. First avpair specifies - -- name=value mapping. - let ((nm,val):attrs') = kvps - let attrs = map (first toCI) attrs' - - -- and we'll gather the rest of the fields with helper functions. - return $ foldl' field (nullCookie nm val) attrs - - - where - nullCookie nm val = Cookie nm val Nothing Nothing Nothing - - fieldFuncs :: [ (CIByteString, Cookie -> ByteString -> Cookie) ] - fieldFuncs = [ ("domain", domain) - , ("expires", expires) - , ("path", path) ] - - domain c d = c { cookieDomain = Just d } - path c p = c { cookiePath = Just p } - expires c e = c { cookieExpires = parseExpires e } - parseExpires e = parseTime defaultTimeLocale - "%a, %d-%b-%Y %H:%M:%S GMT" - (map w2c $ S.unpack e) - - field c (k,v) = fromMaybe c (flip ($ c) v <$> lookup k fieldFuncs) - - --- unhelpfully, the spec mentions "old-style" cookies that don't have quotes --- around the value. wonderful. -pWord :: Parser ByteString -pWord = pQuotedString <|> (takeWhile ((/= ';') . w2c)) - -pAvPairs :: Parser [(ByteString, ByteString)] -pAvPairs = do - a <- pAvPair - b <- many (pSpaces *> char ';' *> pSpaces *> pAvPair) - - return $ a:b - -pAvPair :: Parser (ByteString, ByteString) -pAvPair = do - key <- pToken <* pSpaces - val <- option "" $ char '=' *> pSpaces *> pWord - - return (key,val) - -parseCookie :: ByteString -> Maybe Cookie -parseCookie = parseToCompletion pCookie - ------------------------------------------------------------------------------- --- MULTIPART/FORMDATA ------------------------------------------------------------------------------- - -parseUrlEncoded :: ByteString -> Map ByteString [ByteString] -parseUrlEncoded s = foldl' (\m (k,v) -> Map.insertWith' (++) k [v] m) - Map.empty - decoded - where - breakApart = (second (S.drop 1)) . S.break (== (c2w '=')) - - parts :: [(ByteString,ByteString)] - parts = map breakApart $ S.split (c2w '&') s - - urldecode = parseToCompletion pUrlEscaped - - decodeOne (a,b) = do - a' <- urldecode a - b' <- urldecode b - return (a',b') - - decoded = catMaybes $ map decodeOne parts - - ------------------------------------------------------------------------------- --- utility functions ------------------------------------------------------------------------------- - -strictize :: L.ByteString -> ByteString -strictize = S.concat . L.toChunks - ------------------------------------------------------------------------------- -char :: Char -> Parser Word8 -char = word8 . c2w - -- diff --git a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-Date.html b/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-Date.html deleted file mode 100644 index 40a5e91..0000000 --- a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-Date.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} - -module Snap.Internal.Http.Server.Date -( getDateString -, getLogDateString -, getCurrentDateTime) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.IORef -import Data.Time.Clock -import Data.Time.LocalTime -import Data.Time.Format -import System.IO.Unsafe -import System.Locale - - --- Here comes a dirty hack. We don't want to be wasting context switches --- building date strings, so we're only going to compute one every two --- seconds. (Approximate timestamps to within a couple of seconds are OK here, --- and we'll reduce overhead.) --- --- Note that we also don't want to wake up a potentially sleeping CPU by just --- running the computation on a timer. We'll allow client traffic to trigger --- the process. - -data DateState = DateState { - _cachedDateString :: !(IORef ByteString) - , _cachedLogString :: !(IORef ByteString) - , _cachedDate :: !(IORef UTCTime) - , _valueIsOld :: !(IORef Bool) - , _morePlease :: !(MVar ()) - , _dataAvailable :: !(MVar ()) - , _dateThread :: !(MVar ThreadId) - } - -dateState :: DateState -dateState = unsafePerformIO $ do - (s1,s2,date) <- fetchTime - bs1 <- newIORef s1 - bs2 <- newIORef s2 - dt <- newIORef date - ov <- newIORef False - th <- newEmptyMVar - mp <- newMVar () - da <- newMVar () - - let d = DateState bs1 bs2 dt ov mp da th - - t <- forkIO $ dateThread d - putMVar th t - - return d - - -fetchTime :: IO (ByteString,ByteString,UTCTime) -fetchTime = do - now <- getCurrentTime - zt <- liftM zonedTimeToLocalTime getZonedTime - return (t1 now, t2 zt, now) - where - t1 now = B.pack $ map c2w $ - formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" now - t2 now = B.pack $ map c2w $ - formatTime defaultTimeLocale "%d/%b/%Y:%H:%M:%S %z" now - -dateThread :: DateState -> IO () -dateThread ds@(DateState dateString logString time valueIsOld morePlease - dataAvailable _) = do - -- a lot of effort to make sure we don't deadlock - takeMVar morePlease - - (s1,s2,now) <- fetchTime - atomicModifyIORef dateString $ const (s1,()) - atomicModifyIORef logString $ const (s2,()) - atomicModifyIORef time $ const (now,()) - - writeIORef valueIsOld False - tryPutMVar dataAvailable () - - threadDelay 2000000 - - takeMVar dataAvailable - writeIORef valueIsOld True - - dateThread ds - -ensureFreshDate :: IO () -ensureFreshDate = block $ do - old <- readIORef $ _valueIsOld dateState - when old $ do - tryPutMVar (_morePlease dateState) () - readMVar $ _dataAvailable dateState - -getDateString :: IO ByteString -getDateString = block $ do - ensureFreshDate - readIORef $ _cachedDateString dateState - - -getLogDateString :: IO ByteString -getLogDateString = block $ do - ensureFreshDate - readIORef $ _cachedLogString dateState - - -getCurrentDateTime :: IO UTCTime -getCurrentDateTime = block $ do - ensureFreshDate - readIORef $ _cachedDate dateState - -- diff --git a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html b/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html deleted file mode 100644 index 8348ccf..0000000 --- a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html +++ /dev/null @@ -1,643 +0,0 @@ - - - - -
{-# LINE 1 "src/Snap/Internal/Http/Server/LibevBackend.hsc" #-} -{-# LANGUAGE BangPatterns #-} -{-# LINE 2 "src/Snap/Internal/Http/Server/LibevBackend.hsc" #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server.LibevBackend -( Backend -, BackendTerminatedException -, Connection -, TimeoutException -, debug -, bindIt -, new -, stop -, withConnection -, sendFile -, getReadEnd -, getWriteEnd -, getRemoteAddr -, getRemotePort -, getLocalAddr -, getLocalPort -) where - ---------------------------- --- TODO: document module -- ---------------------------- - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Unsafe as B -import qualified Data.ByteString as B -import Data.IORef -import Data.Iteratee.WrappedByteString -import Data.Set (Set) -import qualified Data.Set as Set -import Data.Typeable -import Foreign hiding (new) -import Foreign.C.Error -import Foreign.C.Types -import GHC.Conc (forkOnIO) -import Network.Libev -import Network.Socket -import qualified Network.Socket.SendFile as SF -import Prelude hiding (catch) -import System.Timeout ------------------------------------------------------------------------------- -import Snap.Iteratee -import Snap.Internal.Debug - - -data Backend = Backend - { _acceptSocket :: Socket - , _acceptFd :: CInt - , _connectionQueue :: Chan CInt - , _evLoop :: EvLoopPtr - , _acceptIOCallback :: FunPtr IoCallback - , _acceptIOObj :: EvIoPtr - - -- FIXME: we don't need _loopThread - , _loopThread :: MVar ThreadId - , _mutexCallbacks :: (FunPtr MutexCallback, FunPtr MutexCallback) - , _loopLock :: MVar () - , _asyncCb :: FunPtr AsyncCallback - , _asyncObj :: EvAsyncPtr - , _killCb :: FunPtr AsyncCallback - , _killObj :: EvAsyncPtr - , _connectionThreads :: MVar (Set ThreadId) - , _backendCPU :: Int - } - - -data Connection = Connection - { _backend :: Backend - , _socket :: Socket - , _socketFd :: CInt - , _remoteAddr :: ByteString - , _remotePort :: Int - , _localAddr :: ByteString - , _localPort :: Int - , _readAvailable :: MVar () - , _writeAvailable :: MVar () - , _timerObj :: EvTimerPtr - , _timerCallback :: FunPtr TimerCallback - , _openingTime :: CDouble - , _lastActivity :: IORef CDouble - , _connIOObj :: EvIoPtr - , _connIOCallback :: FunPtr IoCallback - , _connThread :: MVar ThreadId - } - - -sendFile :: Connection -> FilePath -> IO () -sendFile c fp = do - withMVar lock $ \_ -> evIoStop loop io - SF.sendFile s fp - withMVar lock $ \_ -> do - tryPutMVar (_readAvailable c) () - tryPutMVar (_writeAvailable c) () - evIoStart loop io - evAsyncSend loop asy - - where - s = _socket c - io = _connIOObj c - b = _backend c - loop = _evLoop b - lock = _loopLock b - asy = _asyncObj b - - -bindIt :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> IO (Socket,CInt) -bindIt bindAddress bindPort = do - sock <- socket AF_INET Stream 0 - addr <- getHostAddr bindPort bindAddress - setSocketOption sock ReuseAddr 1 - bindSocket sock addr - listen sock bindPort - let sockFd = fdSocket sock - c_setnonblocking sockFd - return (sock, sockFd) - - -new :: (Socket,CInt) -- ^ value you got from bindIt - -> Int -- ^ cpu - -> IO Backend -new (sock,sockFd) cpu = do - connq <- newChan - - -- We'll try kqueue on OSX even though the libev docs complain that it's - -- "broken", in the hope that it works as expected for sockets - f <- evRecommendedBackends - lp <- evLoopNew $ toEnum . fromEnum $ f .|. evbackend_kqueue - - - -- we'll be working multithreaded so we need to set up locking for the C - -- event loop struct - (mc1,mc2,looplock) <- setupLockingForLoop lp - - -- setup async callbacks -- these allow us to wake up the main loop - -- (normally blocked in c-land) from other threads - asyncObj <- mkEvAsync - asyncCB <- mkAsyncCallback $ \_ _ _ -> do - debug "async wakeup" - return () - - killObj <- mkEvAsync - killCB <- mkAsyncCallback $ \_ _ _ -> do - debug "async kill wakeup" - evUnloop lp 2 - return () - - evAsyncInit asyncObj asyncCB - evAsyncStart lp asyncObj - evAsyncInit killObj killCB - evAsyncStart lp killObj - - -- setup the accept callback; this watches for read readiness on the listen - -- port - accCB <- mkIoCallback $ acceptCallback sockFd connq - accIO <- mkEvIo - evIoInit accIO accCB sockFd ev_read - evIoStart lp accIO - - -- an MVar for the loop thread, and one to keep track of the set of active - -- threads - threadMVar <- newEmptyMVar - threadSetMVar <- newMVar Set.empty - - let b = Backend sock - sockFd - connq - lp - accCB - accIO - threadMVar - (mc1,mc2) - looplock - asyncCB - asyncObj - killCB - killObj - threadSetMVar - cpu - - tid <- forkOnIO cpu $ loopThread b - putMVar threadMVar tid - - debug $ "Backend.new: loop spawned" - return b - - --- | Run evLoop in a thread -loopThread :: Backend -> IO () -loopThread backend = do - debug $ "starting loop" - (ignoreException go) `finally` cleanup - debug $ "loop finished" - where - cleanup = do - debug $ "loopThread: cleaning up" - ignoreException $ freeBackend backend - lock = _loopLock backend - loop = _evLoop backend - go = takeMVar lock >> block (evLoop loop 0) - - -acceptCallback :: CInt -> Chan CInt -> IoCallback -acceptCallback accFd chan _loopPtr _ioPtr _ = do - r <- c_accept accFd - - case r of - -- this (EWOULDBLOCK) shouldn't happen (we just got told it was ready!), - -- if it does (maybe the request got picked up by another thread) we'll - -- just bail out - -2 -> return () - -1 -> debugErrno "Backend.acceptCallback:c_accept()" - fd -> writeChan chan fd - - -ioCallback :: MVar () -> MVar () -> IoCallback -ioCallback ra wa _loopPtr _ioPtr event = do - -- send notifications to the worker thread - when isRead $ tryPutMVar ra () >> return () - when isWrite $ tryPutMVar wa () >> return () - - where - isRead = (event .&. ev_read) /= 0 - isWrite = (event .&. ev_write) /= 0 - - -seconds :: Int -> Int -seconds n = n * ((10::Int)^(6::Int)) - - -stop :: Backend -> IO () -stop b = ignoreException $ do - debug $ "Backend.stop" - - -- FIXME: what are we gonna do here? - -- - -- 1. take the loop lock - -- 2. shut down the accept() callback - -- 3. stuff a poison pill (a bunch of -1 values should do) down the - -- connection queue so that withConnection knows to throw an exception - -- back up to its caller - -- 4. release the loop lock - -- 5. wait until all of the threads have finished, or until 10 seconds have - -- elapsed, whichever comes first - -- 6. take the loop lock - -- 7. call evUnloop and wake up the loop using evAsyncSend - -- 8. release the loop lock, the main loop thread should then free/clean - -- everything up (threads, connections, io objects, callbacks, etc) - - withMVar lock $ \_ -> do - evIoStop loop acceptObj - replicateM_ 10 $ writeChan connQ (-1) - - debug $ "Backend.stop: waiting at most 10 seconds for connection threads to die" - waitForThreads b $ seconds 10 - debug $ "Backend.stop: all threads dead, unlooping" - - withMVar lock $ \_ -> do - -- FIXME: hlibev should export EVUNLOOP_ALL - evUnloop loop 2 - evAsyncSend loop killObj - - debug $ "unloop sent" - - - where - loop = _evLoop b - acceptObj = _acceptIOObj b - killObj = _killObj b - lock = _loopLock b - connQ = _connectionQueue b - - - -waitForThreads :: Backend -> Int -> IO () -waitForThreads backend t = timeout t wait >> return () - where - threadSet = _connectionThreads backend - wait = do - threads <- readMVar threadSet - if (Set.null threads) - then return () - else threadDelay (seconds 1) >> wait - - - -getAddr :: SockAddr -> IO (ByteString, Int) -getAddr addr = - case addr of - SockAddrInet p ha -> do - s <- liftM (B.pack . map c2w) (inet_ntoa ha) - return (s, fromIntegral p) - - a -> throwIO $ AddressNotSupportedException (show a) - - --- | throw a timeout exception to the handling thread -- it'll clean up --- everything -timerCallback :: MVar ThreadId -> TimerCallback -timerCallback tmv _ _ _ = do - tid <- readMVar tmv - throwTo tid TimeoutException - - -freeConnection :: Connection -> IO () -freeConnection conn = ignoreException $ do - withMVar loopLock $ \_ -> block $ do - -- close socket (twice to get proper linger behaviour) - c_close fd - c_close fd - - -- stop and free timer object - evTimerStop loop timerObj - freeEvTimer timerObj - freeTimerCallback timerCb - - -- stop and free i/o object - evIoStop loop ioObj - freeEvIo ioObj - freeIoCallback ioCb - - -- remove the thread id from the backend set - tid <- readMVar threadMVar - modifyMVar_ tsetMVar $ return . Set.delete tid - - -- wake up the event loop so it can be apprised of the changes - evAsyncSend loop asyncObj - - where - backend = _backend conn - tsetMVar = _connectionThreads backend - loop = _evLoop backend - loopLock = _loopLock backend - asyncObj = _asyncObj backend - - fd = _socketFd conn - threadMVar = _connThread conn - ioObj = _connIOObj conn - ioCb = _connIOCallback conn - timerObj = _timerObj conn - timerCb = _timerCallback conn - - -ignoreException :: IO () -> IO () -ignoreException = handle (\(_::SomeException) -> return ()) - - -freeBackend :: Backend -> IO () -freeBackend backend = ignoreException $ block $ do - -- note: we only get here after an unloop - - withMVar tsetMVar $ \set -> do - mapM_ killThread $ Set.toList set - - debug $ "Backend.freeBackend: wait at most 2 seconds for threads to die" - waitForThreads backend $ seconds 2 - - debug $ "Backend.freeBackend: all threads dead" - - debug $ "Backend.freeBackend: destroying resources" - freeEvIo acceptObj - freeIoCallback acceptCb - c_close fd - - evAsyncStop loop asyncObj - freeEvAsync asyncObj - freeAsyncCallback asyncCb - - evAsyncStop loop killObj - freeEvAsync killObj - freeAsyncCallback killCb - - freeMutexCallback mcb1 - freeMutexCallback mcb2 - - evLoopDestroy loop - debug $ "Backend.freeBackend: resources destroyed" - - where - fd = _acceptFd backend - acceptObj = _acceptIOObj backend - acceptCb = _acceptIOCallback backend - tsetMVar = _connectionThreads backend - asyncObj = _asyncObj backend - asyncCb = _asyncCb backend - killObj = _killObj backend - killCb = _killCb backend - (mcb1,mcb2) = _mutexCallbacks backend - loop = _evLoop backend - - --- | Note: proc gets run in the background -withConnection :: Backend -> Int -> (Connection -> IO ()) -> IO () -withConnection backend cpu proc = go - where - threadProc conn = ignoreException (proc conn) `finally` freeConnection conn - - go = do - fd <- readChan $ _connectionQueue backend - - -- if fd < 0 throw an exception here (because this only happens if stop - -- is called) - when (fd < 0) $ throwIO BackendTerminatedException - - sock <- mkSocket fd AF_INET Stream 0 Connected - peerName <- getPeerName sock - sockName <- getSocketName sock - - -- set_linger fd - c_setnonblocking fd - - (remoteAddr, remotePort) <- getAddr peerName - (localAddr, localPort) <- getAddr sockName - - let lp = _evLoop backend - - now <- evNow lp - lastActRef <- newIORef now - - -- makes sense to assume the socket is read/write available when - -- opened; worst-case is we get EWOULDBLOCK - ra <- newMVar () - wa <- newMVar () - - tmr <- mkEvTimer - thrmv <- newEmptyMVar - tcb <- mkTimerCallback $ timerCallback thrmv - evTimerInit tmr tcb 20 0 - - evio <- mkEvIo - iocb <- mkIoCallback $ ioCallback ra wa - evIoInit evio iocb fd (ev_read .|. ev_write) - - -- take ev_loop lock, start timer and io watchers - withMVar (_loopLock backend) $ \_ -> do - evTimerStart lp tmr - evIoStart lp evio - - -- wakeup the loop thread so that these new watchers get - -- registered next time through the loop - evAsyncSend lp $ _asyncObj backend - - let conn = Connection backend - sock - fd - remoteAddr - remotePort - localAddr - localPort - ra - wa - tmr - tcb - now - lastActRef - evio - iocb - thrmv - - - tid <- forkOnIO cpu $ threadProc conn - - modifyMVar_ (_connectionThreads backend) $ ins tid - putMVar thrmv tid - - where - ins !thr !s = let !r = Set.insert thr s in return (r `seq` r) - - -data BackendTerminatedException = BackendTerminatedException - deriving (Typeable) - -instance Show BackendTerminatedException where - show BackendTerminatedException = "Backend terminated" - -instance Exception BackendTerminatedException - - - -data AddressNotSupportedException = AddressNotSupportedException String - deriving (Typeable) - -instance Show AddressNotSupportedException where - show (AddressNotSupportedException x) = "Address not supported: " ++ x - -instance Exception AddressNotSupportedException - - -getRemoteAddr :: Connection -> ByteString -getRemoteAddr = _remoteAddr - -getRemotePort :: Connection -> Int -getRemotePort = _remotePort - -getLocalAddr :: Connection -> ByteString -getLocalAddr = _localAddr - -getLocalPort :: Connection -> Int -getLocalPort = _localPort - ------------------------------------------------------------------------------- - --- fixme: new function name -getHostAddr :: Int - -> ByteString - -> IO SockAddr -getHostAddr p s = do - h <- if s == "*" - then return iNADDR_ANY - else inet_addr (map w2c . B.unpack $ s) - - return $ SockAddrInet (fromIntegral p) h - - - -bLOCKSIZE :: Int -bLOCKSIZE = 8192 - - -data TimeoutException = TimeoutException - deriving (Typeable) - -instance Show TimeoutException where - show _ = "timeout" - -instance Exception TimeoutException - - -recvData :: Connection -> Int -> IO ByteString -recvData conn n = do - dbg "entered" - allocaBytes n $ \cstr -> do - sz <- throwErrnoIfMinus1RetryMayBlock - "recvData" - (c_read fd cstr (toEnum n)) - waitForLock - - dbg $ "sz returned " ++ show sz - - if sz == 0 - then return "" - else B.packCStringLen ((castPtr cstr),(fromEnum sz)) - - where - dbg s = debug $ "Backend.recvData(" ++ show (_socketFd conn) ++ "): " ++ s - - fd = _socketFd conn - lock = _readAvailable conn - waitForLock = do - dbg "waitForLock" - takeMVar lock - - -sendData :: Connection -> ByteString -> IO () -sendData conn bs = do - let len = B.length bs - dbg $ "entered w/ " ++ show len ++ " bytes" - written <- B.unsafeUseAsCString bs $ \cstr -> - throwErrnoIfMinus1RetryMayBlock - "sendData" - (c_write fd cstr (toEnum len)) - waitForLock - - dbg $ "wrote " ++ show written ++ " bytes" - - let n = fromEnum written - if n < len - then sendData conn $ B.drop n bs - else return () - - where - dbg s = debug $ "Backend.sendData(" ++ show (_socketFd conn) ++ "): " ++ s - fd = _socketFd conn - lock = _writeAvailable conn - waitForLock = takeMVar lock - - -getReadEnd :: Connection -> Enumerator IO a -getReadEnd = enumerate - - -getWriteEnd :: Connection -> Iteratee IO () -getWriteEnd = writeOut - - -enumerate :: (MonadIO m) => Connection -> Enumerator m a -enumerate = loop - where - loop conn f = do - s <- liftIO $ recvData conn bLOCKSIZE - sendOne conn f s - - sendOne conn f s = do - v <- runIter f (if B.null s - then EOF Nothing - else Chunk $ WrapBS s) - case v of - r@(Done _ _) -> return $ liftI r - (Cont k Nothing) -> loop conn k - (Cont _ (Just e)) -> return $ throwErr e - - -writeOut :: (MonadIO m) => Connection -> Iteratee m () -writeOut conn = IterateeG out - where - out c@(EOF _) = return $ Done () c - - out (Chunk s) = do - let x = unWrap s - - ee <- liftIO $ ((try $ sendData conn x) - :: IO (Either SomeException ())) - - case ee of - (Left e) -> return $ Done () (EOF $ Just $ Err $ show e) - (Right _) -> return $ Cont (writeOut conn) Nothing - -- diff --git a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server.html b/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server.html deleted file mode 100644 index e51b977..0000000 --- a/static/docs/0.1.1/snap-server/src/Snap-Internal-Http-Server.html +++ /dev/null @@ -1,633 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server where - ------------------------------------------------------------------------------- -import Control.Arrow (first, second) -import Control.Monad.State.Strict -import Control.Concurrent.MVar -import Control.Exception -import Data.Char -import Data.CIByteString -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Lazy as L -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Nums.Careless.Int as Cvt -import Data.IORef -import Data.List (foldl') -import qualified Data.Map as Map -import Data.Maybe (fromJust, catMaybes, fromMaybe) -import Data.Monoid -import GHC.Conc -import Prelude hiding (catch, show, Show) -import qualified Prelude -import System.Posix.Files hiding (setFileSize) -import Text.Show.ByteString hiding (runPut) ------------------------------------------------------------------------------- -import System.FastLogger -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Internal.Http.Parser -import Snap.Iteratee hiding (foldl', head, take) -import qualified Snap.Iteratee as I - -#ifdef LIBEV -import qualified Snap.Internal.Http.Server.LibevBackend as Backend -import Snap.Internal.Http.Server.LibevBackend (debug) -#else -import qualified Snap.Internal.Http.Server.SimpleBackend as Backend -import Snap.Internal.Http.Server.SimpleBackend (debug) -#endif - -import Snap.Internal.Http.Server.Date - ------------------------------------------------------------------------------- --- | The handler has to return the request object because we have to clear the --- HTTP request body before we send the response. If the handler consumes the --- request body, it is responsible for setting @rqBody=return@ in the returned --- request (otherwise we will mess up reading the input stream). --- --- Note that we won't be bothering end users with this -- the details will be --- hidden inside the Snap monad -type ServerHandler = Request -> Iteratee IO (Request,Response) - -type ServerMonad = StateT ServerState (Iteratee IO) - -data ServerState = ServerState - { _forceConnectionClose :: Bool - , _localHostname :: ByteString - , _localAddress :: ByteString - , _localPort :: Int - , _remoteAddr :: ByteString - , _remotePort :: Int - , _logAccess :: Request -> Response -> IO () - , _logError :: ByteString -> IO () - } - - ------------------------------------------------------------------------------- -runServerMonad :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> (Request -> Response -> IO ()) -- ^ access log function - -> (ByteString -> IO ()) -- ^ error log function - -> ServerMonad a -- ^ monadic action to run - -> Iteratee IO a -runServerMonad lh lip lp rip rp la le m = evalStateT m st - where - st = ServerState False lh lip lp rip rp la le - - - ------------------------------------------------------------------------------- --- input/output - - ------------------------------------------------------------------------------- -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the access log - -> Maybe FilePath -- ^ path to the error log - -> ServerHandler -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alogPath elogPath handler = - withLoggers alogPath elogPath - (\(alog, elog) -> spawnAll alog elog) - - where - spawnAll alog elog = do - let n = numCapabilities - bracket (spawn n) - (\xs -> do - logE elog "Server.httpServe: SHUTDOWN" - mapM_ (Backend.stop . fst) xs - logE elog "Server.httpServe: BACKEND STOPPED") - (runAll alog elog) - - - runAll alog elog xs = do - mapM_ f $ xs `zip` [0..] - mapM_ (takeMVar . snd) xs - where - f ((backend,mvar),cpu) = - forkOnIO cpu $ do - labelMe $ map w2c $ S.unpack $ - S.concat ["accThread ", l2s $ show cpu] - (try $ (goooo alog elog backend cpu)) :: IO (Either SomeException ()) - putMVar mvar () - - goooo alog elog backend cpu = - let loop = go alog elog backend cpu >> loop - in loop - - maybeSpawnLogger = maybe (return Nothing) $ (liftM Just) . newLogger - - withLoggers afp efp = - bracket (do alog <- maybeSpawnLogger afp - elog <- maybeSpawnLogger efp - return (alog, elog)) - (\(alog, elog) -> do - threadDelay 1000000 - maybe (return ()) stopLogger alog - maybe (return ()) stopLogger elog) - - labelMe :: String -> IO () - labelMe s = do - tid <- myThreadId - labelThread tid s - - spawn n = do - sock <- Backend.bindIt bindAddress bindPort - backends <- mapM (Backend.new sock) $ [0..(n-1)] - mvars <- replicateM n newEmptyMVar - - return (backends `zip` mvars) - - - runOne alog elog backend cpu = Backend.withConnection backend cpu $ \conn -> do - debug "Server.httpServe.runOne: entered" - let readEnd = Backend.getReadEnd conn - writeEnd <- I.bufferIteratee $ Backend.getWriteEnd conn - - let raddr = Backend.getRemoteAddr conn - let rport = Backend.getRemotePort conn - let laddr = Backend.getLocalAddr conn - let lport = Backend.getLocalPort conn - - runHTTP localHostname laddr lport raddr rport - alog elog readEnd writeEnd (Backend.sendFile conn) - handler - - debug "Server.httpServe.runHTTP: finished" - - - go alog elog backend cpu = runOne alog elog backend cpu - `catches` - [ Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: AsyncException) -> do - logE elog $ - S.concat [ "Server.httpServe.go: got async exception, " - , "terminating:\n", bshow e ] - throwIO e - - , Handler $ \(e :: Backend.BackendTerminatedException) -> do - logE elog $ "Server.httpServe.go: got backend terminated, waiting for cleanup" - throwIO e - - , Handler $ \(e :: IOException) -> do - logE elog $ S.concat [ "Server.httpServe.go: got io exception: " - , bshow e ] - - , Handler $ \(e :: SomeException) -> do - logE elog $ S.concat [ - "Server.httpServe.go: got someexception: " - , bshow e ] - return () ] - ------------------------------------------------------------------------------- -debugE :: (MonadIO m) => ByteString -> m () -debugE s = debug $ "Server: " ++ (map w2c $ S.unpack s) - - ------------------------------------------------------------------------------- -logE :: Maybe Logger -> ByteString -> IO () -logE elog = maybe debugE (\l s -> debugE s >> logE' l s) elog - -logE' :: Logger -> ByteString -> IO () -logE' logger s = (timestampedLogEntry s) >>= logMsg logger - - -bshow :: (Prelude.Show a) => a -> ByteString -bshow = toBS . Prelude.show - ------------------------------------------------------------------------------- -logA ::Maybe Logger -> Request -> Response -> IO () -logA alog = maybe (\_ _ -> return ()) logA' alog - -logA' :: Logger -> Request -> Response -> IO () -logA' logger req rsp = do - let hdrs = rqHeaders req - let host = rqRemoteAddr req - let user = Nothing -- TODO we don't do authentication yet - let (v, v') = rqVersion req - let ver = S.concat [ "HTTP/", bshow v, ".", bshow v' ] - let method = toBS $ Prelude.show (rqMethod req) - let reql = S.intercalate " " [ method, rqURI req, ver ] - let status = rspStatus rsp - let cl = rspContentLength rsp - let referer = maybe Nothing (Just . head) $ Map.lookup "referer" hdrs - let userAgent = maybe "-" head $ Map.lookup "user-agent" hdrs - - msg <- combinedLogEntry host user reql status cl referer userAgent - logMsg logger msg - - ------------------------------------------------------------------------------- -runHTTP :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> Maybe Logger -- ^ access logger - -> Maybe Logger -- ^ error logger - -> Enumerator IO () -- ^ read end of socket - -> Iteratee IO () -- ^ write end of socket - -> (FilePath -> IO ()) -- ^ sendfile end - -> ServerHandler -- ^ handler procedure - -> IO () -runHTTP lh lip lp rip rp alog elog readEnd writeEnd onSendFile handler = - go `catches` [ Handler $ \(e :: AsyncException) -> do - throwIO e - - , Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: SomeException) -> - logE elog $ toBS $ Prelude.show e ] - - where - go = do - let iter = runServerMonad lh lip lp rip rp (logA alog) (logE elog) $ - httpSession writeEnd onSendFile handler - readEnd iter >>= run - - ------------------------------------------------------------------------------- -sERVER_HEADER :: [ByteString] -sERVER_HEADER = ["Snap/0.pre-1"] - - ------------------------------------------------------------------------------- -logAccess :: Request -> Response -> ServerMonad () -logAccess req rsp = gets _logAccess >>= (\l -> liftIO $ l req rsp) - ------------------------------------------------------------------------------- -logError :: ByteString -> ServerMonad () -logError s = gets _logError >>= (\l -> liftIO $ l s) - ------------------------------------------------------------------------------- --- | Runs an HTTP session. -httpSession :: Iteratee IO () -- ^ write end of socket - -> (FilePath -> IO ()) -- ^ sendfile continuation - -> ServerHandler -- ^ handler procedure - -> ServerMonad () -httpSession writeEnd onSendFile handler = do - liftIO $ debug "Server.httpSession: entered" - mreq <- receiveRequest - - case mreq of - (Just req) -> do - (req',rspOrig) <- lift $ handler req - let rspTmp = rspOrig { rspHttpVersion = rqVersion req } - checkConnectionClose (rspHttpVersion rspTmp) (rspHeaders rspTmp) - - cc <- gets _forceConnectionClose - let rsp = if cc - then (setHeader "Connection" "close" rspTmp) - else rspTmp - - - liftIO $ debug "Server.httpSession: handled, skipping request body" - srqEnum <- liftIO $ readIORef $ rqBody req' - let (SomeEnumerator rqEnum) = srqEnum - lift $ joinIM $ rqEnum skipToEof - liftIO $ debug "Server.httpSession: request body skipped, sending response" - - date <- liftIO getDateString - let ins = (Map.insert "Date" [date] . Map.insert "Server" sERVER_HEADER) - let rsp' = updateHeaders ins rsp - (bytesSent,_) <- sendResponse rsp' writeEnd onSendFile - - maybe (logAccess req rsp') - (\_ -> logAccess req $ setContentLength bytesSent rsp') - (rspContentLength rsp') - - if cc - then return () - else httpSession writeEnd onSendFile handler - - Nothing -> return () - ------------------------------------------------------------------------------- -receiveRequest :: ServerMonad (Maybe Request) -receiveRequest = do - mreq <- lift parseRequest - - case mreq of - (Just ireq) -> do - req' <- toRequest ireq - setEnumerator req' - req <- parseForm req' - checkConnectionClose (rqVersion req) (rqHeaders req) - return $ Just req - - Nothing -> return Nothing - - - where - -- check: did the client specify "transfer-encoding: chunked"? then we have - -- to honor that. - -- - -- otherwise: check content-length header. if set: only take N bytes from - -- the read end of the socket - -- - -- if no content-length and no chunked encoding, enumerate the entire - -- socket and close afterwards - setEnumerator :: Request -> ServerMonad () - setEnumerator req = - if isChunked - then liftIO $ writeIORef (rqBody req) - (SomeEnumerator readChunkedTransferEncoding) - else maybe noContentLength hasContentLength mbCL - - where - isChunked = maybe False - ((== ["chunked"]) . map toCI) - (Map.lookup "transfer-encoding" hdrs) - - hasContentLength :: Int -> ServerMonad () - hasContentLength l = do - liftIO $ writeIORef (rqBody req) - (SomeEnumerator e) - where - e :: Enumerator IO a - e = return . joinI . I.take l - - noContentLength :: ServerMonad () - noContentLength = - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . joinI . I.take 0 ) - - - hdrs = rqHeaders req - mbCL = Map.lookup "content-length" hdrs >>= return . Cvt.int . head - - - parseForm :: Request -> ServerMonad Request - parseForm req = if doIt then getIt else return req - where - doIt = mbCT == Just "application/x-www-form-urlencoded" - mbCT = liftM head $ Map.lookup "content-type" (rqHeaders req) - - maximumPOSTBodySize :: Int - maximumPOSTBodySize = 10*1024*1024 - - getIt :: ServerMonad Request - getIt = do - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - let i = joinI $ takeNoMoreThan maximumPOSTBodySize stream2stream - iter <- liftIO $ enum i - body <- lift iter - let newParams = parseUrlEncoded $ strictize $ fromWrap body - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . I.joinI . I.take 0) - return $ req { rqParams = rqParams req `mappend` newParams } - - - toRequest (IRequest method uri version kvps) = do - localAddr <- gets _localAddress - localPort <- gets _localPort - remoteAddr <- gets _remoteAddr - remotePort <- gets _remotePort - localHostname <- gets _localHostname - - let (serverName, serverPort) = fromMaybe - (localHostname, localPort) - (liftM (parseHost . head) - (Map.lookup "host" hdrs)) - - -- will override in "setEnumerator" - enum <- liftIO $ newIORef $ SomeEnumerator return - - - return $ Request serverName - serverPort - remoteAddr - remotePort - localAddr - localPort - localHostname - isSecure - hdrs - enum - mbContentLength - method - version - cookies - snapletPath - pathInfo - contextPath - uri - queryString - params - - where - snapletPath = "" -- TODO: snaplets in v0.2 - - dropLeadingSlash s = maybe s f mbS - where - f (a,s') = if a == c2w '/' then s' else s - mbS = S.uncons s - - isSecure = False - - hdrs = toHeaders kvps - - mbContentLength = liftM (Cvt.int . head) $ - Map.lookup "content-length" hdrs - - cookies = maybe [] - (catMaybes . map parseCookie) - (Map.lookup "set-cookie" hdrs) - - contextPath = "/" - - parseHost h = (a, Cvt.int (S.drop 1 b)) - where - (a,b) = S.break (== (c2w ':')) h - - params = parseUrlEncoded queryString - - (pathInfo, queryString) = first dropLeadingSlash . second (S.drop 1) $ - S.break (== (c2w '?')) uri - - ------------------------------------------------------------------------------- --- Response must be well-formed here -sendResponse :: Response - -> Iteratee IO a - -> (FilePath -> IO a) - -> ServerMonad (Int,a) -sendResponse rsp' writeEnd onSendFile = do - rsp <- fixupResponse rsp' - let !headerString = mkHeaderString rsp - - (!x,!bs) <- case (rspBody rsp) of - (Enum e) -> liftIO $ whenEnum headerString e - (SendFile f) -> liftIO $ whenSendFile headerString rsp f - - return $! (bs,x) - - where - whenEnum hs e = do - let enum = enumBS hs >. e - let hl = S.length hs - (x,bs) <- liftIO $ enum (countBytes writeEnd) >>= run - - return (x, bs-hl) - - whenSendFile hs r f = do - -- guaranteed to have a content length here. - enumBS hs writeEnd >>= run - - let !cl = fromJust $ rspContentLength r - x <- onSendFile f - return (x, cl) - - (major,minor) = rspHttpVersion rsp' - - - fmtHdrs hdrs = - {-# SCC "fmtHdrs" #-} - concat xs - where - xs = map f $ Map.toList hdrs - - f (k, ys) = map (g k) ys - - g k y = S.concat [ unCI k, ": ", y, "\r\n" ] - - - noCL :: Response -> ServerMonad Response - noCL r = - {-# SCC "noCL" #-} - do - -- are we in HTTP/1.1? - let sendChunked = (rspHttpVersion r) == (1,1) - if sendChunked - then do - let r' = setHeader "Transfer-Encoding" "chunked" r - let e = writeChunkedTransferEncoding $ rspBodyToEnum $ rspBody r - return $ r' { rspBody = Enum e } - - else do - -- HTTP/1.0 and no content-length? We'll have to close the - -- socket. - modify $! \s -> s { _forceConnectionClose = True } - return $ setHeader "Connection" "close" r - - - hasCL :: Int -> Response -> ServerMonad Response - hasCL cl r = - {-# SCC "hasCL" #-} - do - -- set the content-length header - let r' = setHeader "Content-Length" (l2s $ show cl) r - let b = case (rspBody r') of - (Enum e) -> Enum (i e) - (SendFile f) -> SendFile f - - return $ r' { rspBody = b } - - where - i :: Enumerator IO a -> Enumerator IO a - i enum iter = enum (joinI $ takeExactly cl iter) - - - setFileSize :: FilePath -> Response -> ServerMonad Response - setFileSize fp r = - {-# SCC "setFileSize" #-} - do - fs <- liftM fromEnum $ liftIO $ getFileSize fp - return $ r { rspContentLength = Just fs } - - - fixupResponse :: Response -> ServerMonad Response - fixupResponse r = - {-# SCC "fixupResponse" #-} - do - let r' = updateHeaders (Map.delete "Content-Length") r - r'' <- case (rspBody r') of - (Enum _) -> return r' - (SendFile f) -> setFileSize f r' - case (rspContentLength r'') of - Nothing -> noCL r'' - (Just sz) -> hasCL sz r'' - - - bsshow = l2s . show - - - mkHeaderString :: Response -> ByteString - mkHeaderString r = - {-# SCC "mkHeaderString" #-} - S.concat $ concat [hl, hdr, eol] - where - hl = [ "HTTP/" - , bsshow major - , "." - , bsshow minor - , " " - , bsshow $ rspStatus r - , " " - , rspStatusReason r - , "\r\n" ] - - hdr = fmtHdrs $ headers r - - eol = ["\r\n"] - - ------------------------------------------------------------------------------- -checkConnectionClose :: (Int, Int) -> Headers -> ServerMonad () -checkConnectionClose ver hdrs = - -- For HTTP/1.1: - -- if there is an explicit Connection: close, close the socket. - -- For HTTP/1.0: - -- if there is no explicit Connection: Keep-Alive, close the socket. - if (ver == (1,1) && l == Just ["close"]) || - (ver == (1,0) && l /= Just ["Keep-Alive"]) - then modify $ \s -> s { _forceConnectionClose = True } - else return () - where - l = liftM (map tl) $ Map.lookup "Connection" hdrs - tl = S.map (c2w . toLower . w2c) - - ------------------------------------------------------------------------------- --- FIXME: whitespace-trim the values here. -toHeaders :: [(ByteString,ByteString)] -> Headers -toHeaders kvps = foldl' f Map.empty kvps' - where - kvps' = map (first toCI . second (:[])) kvps - f m (k,v) = Map.insertWith' (flip (++)) k v m - - ------------------------------------------------------------------------------- -getFileSize :: FilePath -> IO FileOffset -getFileSize fp = liftM fileSize $ getFileStatus fp - - -l2s :: L.ByteString -> S.ByteString -l2s = S.concat . L.toChunks - - -toBS :: String -> ByteString -toBS = S.pack . map c2w -- diff --git a/static/docs/0.1.1/snap-server/src/System-FastLogger.html b/static/docs/0.1.1/snap-server/src/System-FastLogger.html deleted file mode 100644 index 0a9d919..0000000 --- a/static/docs/0.1.1/snap-server/src/System-FastLogger.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module System.FastLogger -( Logger -, timestampedLogEntry -, combinedLogEntry -, newLogger -, logMsg -, stopLogger -) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Internal (c2w) -import Data.DList (DList) -import qualified Data.DList as D -import Data.IORef -import Data.Maybe -import Data.Serialize.Put -import Data.Time.Clock -import Prelude hiding (catch, show) -import qualified Prelude -import System.IO -import Text.Show.ByteString hiding (runPut) - -import Snap.Internal.Http.Server.Date - - --- | Holds the state for a logger. -data Logger = Logger - { _queuedMessages :: !(IORef (DList ByteString)) - , _dataWaiting :: !(MVar ()) - , _loggerPath :: !(FilePath) - , _loggingThread :: !(MVar ThreadId) } - - --- | Creates a new logger, logging to the given file. If the file argument is --- \"-\", then log to stdout; if it's \"stderr\" then we log to stderr, --- otherwise we log to a regular file in append mode. The file is closed and --- re-opened every 15 minutes to facilitate external log rotation. -newLogger :: FilePath -> IO Logger -newLogger fp = do - q <- newIORef D.empty - dw <- newEmptyMVar - th <- newEmptyMVar - - let lg = Logger q dw fp th - - tid <- forkIO $ loggingThread lg - putMVar th tid - - return lg - --- | Prepares a log message with the time prepended. -timestampedLogEntry :: ByteString -> IO ByteString -timestampedLogEntry msg = do - timeStr <- getLogDateString - - return $ runPut $ do - putWord8 $ c2w '[' - putByteString timeStr - putByteString "] " - putByteString msg - - --- | Prepares a log message in \"combined\" format. -combinedLogEntry :: ByteString -- ^ remote host - -> Maybe ByteString -- ^ remote user - -> ByteString -- ^ request line (up to you to ensure - -- there are no quotes in here) - -> Int -- ^ status code - -> Maybe Int -- ^ num bytes sent - -> Maybe ByteString -- ^ referer (up to you to ensure - -- there are no quotes in here) - -> ByteString -- ^ user agent (up to you to ensure - -- there are no quotes in here) - -> IO ByteString -combinedLogEntry host mbUser req status mbNumBytes mbReferer userAgent = do - let user = fromMaybe "-" mbUser - let numBytes = maybe "-" (\s -> strict $ show s) mbNumBytes - let referer = maybe "-" (\s -> S.concat ["\"", s, "\""]) mbReferer - - timeStr <- getLogDateString - - let p = [ host - , " - " - , user - , " [" - , timeStr - , "] \"" - , req - , "\" " - , strict $ show status - , " " - , numBytes - , " " - , referer - , " \"" - , userAgent - , "\"" ] - - return $ S.concat p - - - where - strict = S.concat . L.toChunks - - --- | Sends out a log message verbatim with a newline appended. Note: --- if you want a fancy log message you'll have to format it yourself --- (or use 'combinedLogEntry'). -logMsg :: Logger -> ByteString -> IO () -logMsg lg s = do - let s' = S.snoc s '\n' - atomicModifyIORef (_queuedMessages lg) $ \d -> (D.snoc d s',()) - tryPutMVar (_dataWaiting lg) () >> return () - - -loggingThread :: Logger -> IO () -loggingThread (Logger queue notifier filePath _) = do - initialize >>= go - - where - openIt = if filePath == "-" - then return stdout - else if filePath == "stderr" - then return stderr - else openFile filePath AppendMode - - closeIt h = if filePath == "-" || filePath == "stderr" - then return () - else hClose h - - go (href, lastOpened) = - (loop (href, lastOpened)) - `catches` - [ Handler $ \(_::AsyncException) -> killit (href, lastOpened) - , Handler $ \(e::SomeException) -> do - hPutStrLn stderr $ "logger got exception: " ++ Prelude.show e - threadDelay 20000000 - go (href, lastOpened) ] - - - initialize = do - lh <- openIt - href <- newIORef lh - t <- getCurrentTime - tref <- newIORef t - return (href, tref) - - - killit (href, lastOpened) = do - flushIt (href, lastOpened) - h <- readIORef href - closeIt h - - - flushIt (href, lastOpened) = do - dl <- atomicModifyIORef queue $ \x -> (D.empty,x) - - let msgs = D.toList dl - let s = L.fromChunks msgs - h <- readIORef href - L.hPut h s - hFlush h - - -- close the file every 15 minutes (for log rotation) - t <- getCurrentTime - old <- readIORef lastOpened - - if diffUTCTime t old > 900 - then do - closeIt h - openIt >>= writeIORef href - writeIORef lastOpened t - else return () - - - loop (href, lastOpened) = do - -- wait on the notification mvar - _ <- takeMVar notifier - - -- grab the queued messages and write them out - flushIt (href, lastOpened) - - -- at least five seconds between log dumps - threadDelay 5000000 - - loop (href, lastOpened) - - --- | Kills a logger thread, causing any unwritten contents to be --- flushed out to disk -stopLogger :: Logger -> IO () -stopLogger lg = withMVar (_loggingThread lg) killThread -- diff --git a/static/docs/0.1.1/snap-server/src/hscolour.css b/static/docs/0.1.1/snap-server/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.1/snap-server/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.1.2/heist/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.1.2/heist/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index f794d47..0000000 --- a/static/docs/0.1.2/heist/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,225 +0,0 @@ - - -
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the apply splice. - | |||||
| |||||
Default attribute name for the apply tag. - | |||||
| |||||
Implementation of the apply splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the bind splice. - | |||||
| |||||
Default attribute name for the bind tag. - | |||||
| |||||
Implementation of the bind splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the ignore splice. - | |||||
| |||||
The ignore tag and everything it surrounds disappears in the - rendered output. - | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Default name for the markdown splice. - | |||||||||||||
| |||||||||||||
Implementation of the markdown splice. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
Modifies a TemplateState to include a static tag. - | |||||
| |||||
Clears the static tag state. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Documentation | |||||
module Text.Templating.Heist.Splices.Apply | |||||
module Text.Templating.Heist.Splices.Bind | |||||
module Text.Templating.Heist.Splices.Ignore | |||||
module Text.Templating.Heist.Splices.Markdown | |||||
module Text.Templating.Heist.Splices.Static | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core definitions for the Heist template system. - The Heist template system is based on XML/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - The most important concept in Heist is the Splice. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. Splice is implemented as a type synonym type Splice m = - TemplateMonad m [Node], and TemplateMonad has a function getParamNode - that lets you get the input node. - Suppose you have a place on your page where you want to display a link with - the text "Logout username" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - getUser :: MyAppMonad (Maybe ByteString) that gets the current user. - You can implement this functionality with a Splice as follows: - - import Text.XML.Expat.Tree - - link :: ByteString -> ByteString -> Node - link target text = X.Element "a" [("href", target)] [X.Text text] - - loginLink :: Node - loginLink = link "/login" "Login" - - logoutLink :: ByteString -> Node - logoutLink user = link "/logout" (B.append "Logout " user) - - loginLogoutSplice :: Splice MyAppMonad - loginLogoutSplice = do - user <- lift getUser - return $ [maybe loginLink logoutLink user] - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the TemplateState data structure. The - following code demonstrates how this splice would be used. - mySplices = [ ("loginLogout", loginLogoutSplice) ] - - main = do - ets <- loadTemplates "templates" $ - foldr (uncurry bindSplice) emptyTemplateState mySplices - let ts = either error id ets - t <- runMyAppMonad $ renderTemplate ts "index" - print $ maybe "Page not found" id t - Here we build up our TemplateState by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final TemplateState wrapped in an Either to handle - errors. Then we use this TemplateState to render our templates. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Types - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist templates are XML documents. The hexpat library is polymorphic over - the type of strings, so here we define a Node alias to fix the string - types of the tag names and tag bodies to ByteString. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Splice is a TemplateMonad computation that returns [Node]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Template is a forest of XML nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions and declarations on TemplateState values - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a template to the template state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty template state, with Heist's default splices (<bind> and - <apply>) mapped. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Convenience function for looking up a splice. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the templateMap in a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Traverses the specified directory structure and builds a - TemplateState by loading all the files with a .tpl extension. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hook functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist hooks allow you to modify templates when they are loaded and before - and after they are run. Every time you call one of the addAbcHook - functions the hook is added to onto the processing pipeline. The hooks - processes the template in the order that they were added to the - TemplateState. - The pre-run and post-run hooks are run before and after every template is - run/rendered. You should be careful what code you put in these hooks - because it can significantly affect the performance of your site. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds an on-load hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a pre-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a post-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TemplateMonad functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Stops the recursive processing of splices. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the node currently being processed. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Performs splice processing on a list of nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the current context - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for running splices and templates - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name in the supplied TemplateState and runs - it in the underlying monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name evaluates it. Same as runTemplate except it - runs in TemplateMonad instead of m. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Renders a template from the specified TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Binds a list of constant string splices - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Misc functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a template in the underlying monad. Similar to runSplice - except that templates don't require a Node as a parameter. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reads an XML document from disk. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a TemplateState to include a static tag. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||||||||||
heist-0.1.1: An xhtml templating system | |||||||||||||||||||||||||||||||||||||||
An xhtml templating system - | |||||||||||||||||||||||||||||||||||||||
Modules | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE OverloadedStrings #-} -module Text.Templating.Heist.Constants where - -import Data.ByteString.Char8 (ByteString) -import qualified Data.Map as Map -import Data.Map (Map) - -htmlEntityLookupTable :: Map ByteString ByteString -htmlEntityLookupTable = Map.fromList [ - ("acute" , "\xc2\xb4") - , ("cedil" , "\xc2\xb8") - , ("circ" , "\xcb\x86") - , ("macr" , "\xc2\xaf") - , ("middot" , "\xc2\xb7") - , ("tilde" , "\xcb\x9c") - , ("uml" , "\xc2\xa8") - , ("Aacute" , "\xc3\x81") - , ("aacute" , "\xc3\xa1") - , ("Acirc" , "\xc3\x82") - , ("acirc" , "\xc3\xa2") - , ("AElig" , "\xc3\x86") - , ("aelig" , "\xc3\xa6") - , ("Agrave" , "\xc3\x80") - , ("agrave" , "\xc3\xa0") - , ("Aring" , "\xc3\x85") - , ("aring" , "\xc3\xa5") - , ("Atilde" , "\xc3\x83") - , ("atilde" , "\xc3\xa3") - , ("Auml" , "\xc3\x84") - , ("auml" , "\xc3\xa4") - , ("Ccedil" , "\xc3\x87") - , ("ccedil" , "\xc3\xa7") - , ("Eacute" , "\xc3\x89") - , ("eacute" , "\xc3\xa9") - , ("Ecirc" , "\xc3\x8a") - , ("ecirc" , "\xc3\xaa") - , ("Egrave" , "\xc3\x88") - , ("egrave" , "\xc3\xa8") - , ("ETH" , "\xc3\x90") - , ("eth" , "\xc3\xb0") - , ("Euml" , "\xc3\x8b") - , ("euml" , "\xc3\xab") - , ("Iacute" , "\xc3\x8d") - , ("iacute" , "\xc3\xad") - , ("Icirc" , "\xc3\x8e") - , ("icirc" , "\xc3\xae") - , ("Igrave" , "\xc3\x8c") - , ("igrave" , "\xc3\xac") - , ("Iuml" , "\xc3\x8f") - , ("iuml" , "\xc3\xaf") - , ("Ntilde" , "\xc3\x91") - , ("ntilde" , "\xc3\xb1") - , ("Oacute" , "\xc3\x93") - , ("oacute" , "\xc3\xb3") - , ("Ocirc" , "\xc3\x94") - , ("ocirc" , "\xc3\xb4") - , ("OElig" , "\xc5\x92") - , ("oelig" , "\xc5\x93") - , ("Ograve" , "\xc3\x92") - , ("ograve" , "\xc3\xb2") - , ("Oslash" , "\xc3\x98") - , ("oslash" , "\xc3\xb8") - , ("Otilde" , "\xc3\x95") - , ("otilde" , "\xc3\xb5") - , ("Ouml" , "\xc3\x96") - , ("ouml" , "\xc3\xb6") - , ("Scaron" , "\xc5\xa0") - , ("scaron" , "\xc5\xa1") - , ("szlig" , "\xc3\x9f") - , ("THORN" , "\xc3\x9e") - , ("thorn" , "\xc3\xbe") - , ("Uacute" , "\xc3\x9a") - , ("uacute" , "\xc3\xba") - , ("Ucirc" , "\xc3\x9b") - , ("ucirc" , "\xc3\xbb") - , ("Ugrave" , "\xc3\x99") - , ("ugrave" , "\xc3\xb9") - , ("Uuml" , "\xc3\x9c") - , ("uuml" , "\xc3\xbc") - , ("Yacute" , "\xc3\x9d") - , ("yacute" , "\xc3\xbd") - , ("yuml" , "\xc3\xbf") - , ("Yuml" , "\xc5\xb8") - , ("cent" , "\xc2\xa2") - , ("curren" , "\xc2\xa4") - , ("euro" , "\xe2\x82\xac") - , ("pound" , "\xc2\xa3") - , ("yen" , "\xc2\xa5") - , ("brvbar" , "\xc2\xa6") - , ("bull" , "\xe2\x80\xa2") - , ("copy" , "\xc2\xa9") - , ("dagger" , "\xe2\x80\xa0") - , ("Dagger" , "\xe2\x80\xa1") - , ("frasl" , "\xe2\x81\x84") - , ("hellip" , "\xe2\x80\xa6") - , ("iexcl" , "\xc2\xa1") - , ("image" , "\xe2\x84\x91") - , ("iquest" , "\xc2\xbf") - , ("mdash" , "\xe2\x80\x94") - , ("ndash" , "\xe2\x80\x93") - , ("not" , "\xc2\xac") - , ("oline" , "\xe2\x80\xbe") - , ("ordf" , "\xc2\xaa") - , ("ordm" , "\xc2\xba") - , ("para" , "\xc2\xb6") - , ("permil" , "\xe2\x80\xb0") - , ("prime" , "\xe2\x80\xb2") - , ("Prime" , "\xe2\x80\xb3") - , ("real" , "\xe2\x84\x9c") - , ("reg" , "\xc2\xae") - , ("sect" , "\xc2\xa7") - , ("shy" , "\173") - , ("sup1" , "\xc2\xb9") - , ("trade" , "\xe2\x84\xa2") - , ("weierp" , "\xe2\x84\x98") - , ("bdquo" , "\xe2\x80\x9e") - , ("laquo" , "\xc2\xab") - , ("ldquo" , "\xe2\x80\x9c") - , ("lsaquo" , "\xe2\x80\xb9") - , ("lsquo" , "\xe2\x80\x98") - , ("raquo" , "\xc2\xbb") - , ("rdquo" , "\xe2\x80\x9d") - , ("rsaquo" , "\xe2\x80\xba") - , ("rsquo" , "\xe2\x80\x99") - , ("sbquo" , "\xe2\x80\x9a") - , ("emsp" , "\xe2\x80\x83") - , ("ensp" , "\xe2\x80\x82") - , ("nbsp" , "\x20") - , ("thinsp" , "\xe2\x80\x89") - , ("zwj" , "\xe2\x80\x8d") - , ("zwnj" , "\xe2\x80\x8c") - , ("deg" , "\xc2\xb0") - , ("divide" , "\xc3\xb7") - , ("frac12" , "\xc2\xbd") - , ("frac14" , "\xc2\xbc") - , ("frac34" , "\xc2\xbe") - , ("ge" , "\xe2\x89\xa5") - , ("le" , "\xe2\x89\xa4") - , ("minus" , "\xe2\x88\x92") - , ("sup2" , "\xc2\xb2") - , ("sup3" , "\xc2\xb3") - , ("times" , "\xc3\x97") - , ("alefsym" , "\xe2\x84\xb5") - , ("and" , "\xe2\x88\xa7") - , ("ang" , "\xe2\x88\xa0") - , ("asymp" , "\xe2\x89\x88") - , ("cap" , "\xe2\x88\xa9") - , ("cong" , "\xe2\x89\x85") - , ("cup" , "\xe2\x88\xaa") - , ("empty" , "\xe2\x88\x85") - , ("equiv" , "\xe2\x89\xa1") - , ("exist" , "\xe2\x88\x83") - , ("fnof" , "\xc6\x92") - , ("forall" , "\xe2\x88\x80") - , ("infin" , "\xe2\x88\x9e") - , ("int" , "\xe2\x88\xab") - , ("isin" , "\xe2\x88\x88") - , ("lang" , "\xe3\x80\x88") - , ("lceil" , "\xe2\x8c\x88") - , ("lfloor" , "\xe2\x8c\x8a") - , ("lowast" , "\xe2\x88\x97") - , ("micro" , "\xc2\xb5") - , ("nabla" , "\xe2\x88\x87") - , ("ne" , "\xe2\x89\xa0") - , ("ni" , "\xe2\x88\x8b") - , ("notin" , "\xe2\x88\x89") - , ("nsub" , "\xe2\x8a\x84") - , ("oplus" , "\xe2\x8a\x95") - , ("or" , "\xe2\x88\xa8") - , ("otimes" , "\xe2\x8a\x97") - , ("part" , "\xe2\x88\x82") - , ("perp" , "\xe2\x8a\xa5") - , ("plusmn" , "\xc2\xb1") - , ("prod" , "\xe2\x88\x8f") - , ("prop" , "\xe2\x88\x9d") - , ("radic" , "\xe2\x88\x9a") - , ("rang" , "\xe3\x80\x89") - , ("rceil" , "\xe2\x8c\x89") - , ("rfloor" , "\xe2\x8c\x8b") - , ("sdot" , "\xe2\x8b\x85") - , ("sim" , "\xe2\x88\xbc") - , ("sub" , "\xe2\x8a\x82") - , ("sube" , "\xe2\x8a\x86") - , ("sum" , "\xe2\x88\x91") - , ("sup" , "\xe2\x8a\x83") - , ("supe" , "\xe2\x8a\x87") - , ("there4" , "\xe2\x88\xb4") - , ("Alpha" , "\xce\x91") - , ("alpha" , "\xce\xb1") - , ("Beta" , "\xce\x92") - , ("beta" , "\xce\xb2") - , ("Chi" , "\xce\xa7") - , ("chi" , "\xcf\x87") - , ("Delta" , "\xce\x94") - , ("delta" , "\xce\xb4") - , ("Epsilon" , "\xce\x95") - , ("epsilon" , "\xce\xb5") - , ("Eta" , "\xce\x97") - , ("eta" , "\xce\xb7") - , ("Gamma" , "\xce\x93") - , ("gamma" , "\xce\xb3") - , ("Iota" , "\xce\x99") - , ("iota" , "\xce\xb9") - , ("Kappa" , "\xce\x9a") - , ("kappa" , "\xce\xba") - , ("Lambda" , "\xce\x9b") - , ("lambda" , "\xce\xbb") - , ("Mu" , "\xce\x9c") - , ("mu" , "\xce\xbc") - , ("Nu" , "\xce\x9d") - , ("nu" , "\xce\xbd") - , ("Omega" , "\xce\xa9") - , ("omega" , "\xcf\x89") - , ("Omicron" , "\xce\x9f") - , ("omicron" , "\xce\xbf") - , ("Phi" , "\xce\xa6") - , ("phi" , "\xcf\x86") - , ("Pi" , "\xce\xa0") - , ("pi" , "\xcf\x80") - , ("piv" , "\xcf\x96") - , ("Psi" , "\xce\xa8") - , ("psi" , "\xcf\x88") - , ("Rho" , "\xce\xa1") - , ("rho" , "\xcf\x81") - , ("Sigma" , "\xce\xa3") - , ("sigma" , "\xcf\x83") - , ("sigmaf" , "\xcf\x82") - , ("Tau" , "\xce\xa4") - , ("tau" , "\xcf\x84") - , ("Theta" , "\xce\x98") - , ("theta" , "\xce\xb8") - , ("thetasym" , "\xcf\x91") - , ("upsih" , "\xcf\x92") - , ("Upsilon" , "\xce\xa5") - , ("upsilon" , "\xcf\x85") - , ("Xi" , "\xce\x9e") - , ("xi" , "\xce\xbe") - , ("Zeta" , "\xce\x96") - , ("zeta" , "\xce\xb6") - , ("crarr" , "\xe2\x86\xb5") - , ("darr" , "\xe2\x86\x93") - , ("dArr" , "\xe2\x87\x93") - , ("harr" , "\xe2\x86\x94") - , ("hArr" , "\xe2\x87\x94") - , ("larr" , "\xe2\x86\x90") - , ("lArr" , "\xe2\x87\x90") - , ("rarr" , "\xe2\x86\x92") - , ("rArr" , "\xe2\x87\x92") - , ("uarr" , "\xe2\x86\x91") - , ("uArr" , "\xe2\x87\x91") - , ("clubs" , "\xe2\x99\xa3") - , ("diams" , "\xe2\x99\xa6") - , ("hearts" , "\xe2\x99\xa5") - , ("spades" , "\xe2\x99\xa0") - , ("loz" , "\xe2\x97\x8a") ] -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Internal.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Internal.html deleted file mode 100644 index fa22e13..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Internal.html +++ /dev/null @@ -1,503 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Text.Templating.Heist.Internal where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad.CatchIO -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as L -import Data.Either -import qualified Data.Foldable as F -import Data.List -import qualified Data.Map as Map -import Data.Map (Map) -import Prelude hiding (catch) -import System.Directory.Tree hiding (name) -import Text.XML.Expat.Format -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Constants - ------------------------------------------------------------------------------- --- Types ------------------------------------------------------------------------------- - --- | Heist templates are XML documents. The hexpat library is polymorphic over --- the type of strings, so here we define a 'Node' alias to fix the string --- types of the tag names and tag bodies to 'ByteString'. -type Node = X.Node ByteString ByteString - - ------------------------------------------------------------------------------- --- | A 'Template' is a forest of XML nodes. -type Template = [Node] - - ------------------------------------------------------------------------------- --- | Reversed list of directories -type TPath = [ByteString] - - ------------------------------------------------------------------------------- -type TemplateMap = Map TPath Template - - ------------------------------------------------------------------------------- --- | Holds all the state information needed for template processing: --- --- * a collection of named templates. If you use the @\<apply --- template=\"foo\"\>@ tag to include another template by name, @\"foo\"@ --- is looked up in here. --- --- * the mapping from tag names to 'Splice's. --- --- * a flag to control whether we will recurse during splice processing. --- --- We'll illustrate the recursion flag with a small example template: --- --- > <foo> --- > <bar> --- > ... --- > </bar> --- > </foo> --- --- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ --- splice will result in a list of nodes @L@; if the recursion flag is on we --- will recursively scan @L@ for splices, otherwise @L@ will be included in the --- output verbatim. -data TemplateState m = TemplateState { - -- | A mapping of splice names to splice actions - _spliceMap :: SpliceMap m - -- | A mapping of template names to templates - , _templateMap :: TemplateMap - -- | A flag to control splice recursion - , _recurse :: Bool - , _curContext :: TPath - , _recursionDepth :: Int - , _onLoadHook :: Template -> IO Template - , _preRunHook :: Template -> m Template - , _postRunHook :: Template -> m Template -} - - ------------------------------------------------------------------------------- -instance Eq (TemplateState m) where - a == b = (_recurse a == _recurse b) && - (_templateMap a == _templateMap b) && - (_curContext a == _curContext b) - - ------------------------------------------------------------------------------- --- | 'TemplateMonad' is a monad transformer that gives you access to the 'Node' --- being processed (using the 'MonadReader' instance) as well as holding the --- 'TemplateState' that contains splice and template mappings (accessible --- using the 'MonadState' instance. -newtype TemplateMonad m a = TemplateMonad (RWST Node () (TemplateState m) m a) - deriving ( Monad - , MonadIO - , MonadCatchIO - , MonadReader Node - , MonadState (TemplateState m) ) - - ------------------------------------------------------------------------------- -instance (Monad m) => Monoid (TemplateState m) where - mempty = TemplateState Map.empty Map.empty True [] 0 - return return return - - (TemplateState s1 t1 r1 _ d1 o1 b1 a1) `mappend` - (TemplateState s2 t2 r2 c2 d2 o2 b2 a2) = - TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) - where - s = s1 `mappend` s2 - t = t1 `mappend` t2 - r = r1 && r2 - d = max d1 d2 - - ------------------------------------------------------------------------------- -instance MonadTrans TemplateMonad where - lift = TemplateMonad . lift - ------------------------------------------------------------------------------- --- | A Splice is a TemplateMonad computation that returns [Node]. -type Splice m = TemplateMonad m Template - - ------------------------------------------------------------------------------- --- | SpliceMap associates a name and a Splice. -type SpliceMap m = Map ByteString (Splice m) - - ------------------------------------------------------------------------------- --- TemplateState functions ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- --- | Adds an on-load hook to a `TemplateState`. -addOnLoadHook :: (Monad m) => - (Template -> IO Template) - -> TemplateState m - -> TemplateState m -addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a pre-run hook to a `TemplateState`. -addPreRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a post-run hook to a `TemplateState`. -addPostRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Bind a new splice declaration to a tag name within a 'TemplateState'. -bindSplice :: Monad m => - ByteString -- ^ tag name - -> Splice m -- ^ splice action - -> TemplateState m -- ^ source state - -> TemplateState m -bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a splice. -lookupSplice :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Splice m) -lookupSplice nm ts = Map.lookup nm $ _spliceMap ts - - ------------------------------------------------------------------------------- --- | Converts a path into an array of the elements in reverse order. -splitPaths :: ByteString -> TPath -splitPaths = reverse . B.split '/' - - ------------------------------------------------------------------------------- --- | Does a single template lookup without cascading up. -singleLookup :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm - - ------------------------------------------------------------------------------- --- | Searches for a template by looking in the full path then backing up into each --- of the parent directories until the template is found. -traversePath :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) -traversePath tm path name = - singleLookup tm path name `mplus` - traversePath tm (tail path) name - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a template. -lookupTemplate :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Template, TPath) -lookupTemplate nameStr ts = - f (_templateMap ts) path name - where (name:p) = case splitPaths nameStr of - [] -> [""] - ps -> ps - path = p ++ (_curContext ts) - f = if '/' `B.elem` nameStr - then singleLookup - else traversePath - - ------------------------------------------------------------------------------- --- | Sets the templateMap in a TemplateState. -setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m -setTemplates m ts = ts { _templateMap = m } - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -insertTemplate :: Monad m => - TPath - -> Template - -> TemplateState m - -> TemplateState m -insertTemplate p t st = - setTemplates (Map.insert p t (_templateMap st)) st - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -addTemplate :: Monad m => - ByteString - -> Template - -> TemplateState m - -> TemplateState m -addTemplate n t st = insertTemplate (splitPaths n) t st - - ------------------------------------------------------------------------------- --- | Gets the node currently being processed. -getParamNode :: Monad m => TemplateMonad m Node -getParamNode = ask - - ------------------------------------------------------------------------------- --- | Stops the recursive processing of splices. -stopRecursion :: Monad m => TemplateMonad m () -stopRecursion = modify (\st -> st { _recurse = False }) - - ------------------------------------------------------------------------------- --- | Sets the current context -setContext :: Monad m => TPath -> TemplateMonad m () -setContext c = modify (\st -> st { _curContext = c }) - - ------------------------------------------------------------------------------- --- | Gets the current context -getContext :: Monad m => TemplateMonad m TPath -getContext = gets _curContext - - ------------------------------------------------------------------------------- --- | Performs splice processing on a list of nodes. -runNodeList :: Monad m => [Node] -> Splice m -runNodeList nodes = liftM concat $ sequence (map runNode nodes) - - ------------------------------------------------------------------------------- --- | Performs splice processing on a single node. -runNode :: Monad m => Node -> Splice m -runNode n@(X.Text _) = return [n] -runNode n@(X.Element nm _ ch) = do - s <- liftM (lookupSplice nm) get - maybe runChildren (recurseSplice n) s - - where - runChildren = do - newKids <- runNodeList ch - return [X.modifyChildren (const newKids) n] - - ------------------------------------------------------------------------------- --- | The maximum recursion depth. (Used to prevent infinite loops.) -mAX_RECURSION_DEPTH :: Int -mAX_RECURSION_DEPTH = 20 - - ------------------------------------------------------------------------------- --- | Checks the recursion flag and recurses accordingly. Does not recurse --- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. -recurseSplice :: Monad m => Node -> Splice m -> Splice m -recurseSplice node splice = do - result <- local (const node) splice - ts' <- get - if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH - then do modify (\st -> st { _recursionDepth = _recursionDepth st + 1 }) - res <- runNodeList result - put ts' - return res - else return result - - ------------------------------------------------------------------------------- --- | Runs a splice in the underlying monad. Splices require two --- parameters, the template state, and an input node. -runSplice :: Monad m => - TemplateState m -- ^ The initial template state - -> Node -- ^ The splice's input node - -> Splice m -- ^ The splice - -> m [Node] -runSplice ts node (TemplateMonad splice) = do - (result,_,_) <- runRWST splice node ts - return result - - ------------------------------------------------------------------------------- --- | Runs a template in the underlying monad. Similar to runSplice --- except that templates don't require a Node as a parameter. -runRawTemplate :: Monad m => TemplateState m -> Template -> m [Node] -runRawTemplate ts template = - _preRunHook ts template >>= - runSplice ts (X.Text "") . runNodeList >>= - _postRunHook ts - - ------------------------------------------------------------------------------- --- | Looks up a template name in the supplied 'TemplateState' and runs --- it in the underlying monad. -runTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe [Node]) -runTemplate ts name = - maybe (return Nothing) - (\(t,ctx) -> - return . Just =<< - runRawTemplate (ts {_curContext = ctx}) t) - (lookupTemplate name ts) - - ------------------------------------------------------------------------------- --- | Looks up a template name evaluates it. Same as runTemplate except it --- runs in TemplateMonad instead of m. -evalTemplate :: Monad m - => ByteString - -> TemplateMonad m (Maybe [Node]) -evalTemplate name = do - ts <- get - lift $ runTemplate ts name - - ------------------------------------------------------------------------------- --- | Binds a list of constant string splices -bindStrings :: Monad m - => [(ByteString, ByteString)] - -> TemplateState m - -> TemplateState m -bindStrings pairs ts = foldr add ts pairs - where - add (n,v) = bindSplice n (return [X.Text v]) - - ------------------------------------------------------------------------------- --- | Renders a template with the specified parameters. This is the function --- to use when you want to "call" a template and pass in parameters from code. -callTemplate :: Monad m - => ByteString -- ^ The name of the template - -> [(ByteString, ByteString)] -- ^ Association list of - -- (name,value) parameter pairs - -> TemplateMonad m (Maybe Template) -callTemplate name params = do - modify $ bindStrings params - evalTemplate name - - ------------------------------------------------------------------------------- --- | Renders a template from the specified TemplateState. -renderTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe ByteString) -renderTemplate ts name = do - ns <- runTemplate ts name - return $ (Just . formatList') =<< ns - - ------------------------------------------------------------------------------- -heistExpatOptions :: X.ParserOptions ByteString ByteString -heistExpatOptions = - X.defaultParserOptions { - X.parserEncoding = Just X.UTF8 - , X.entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) - } - ------------------------------------------------------------------------------- --- Template loading ------------------------------------------------------------------------------- - --- | Reads an XML document from disk. -getDoc :: String -> IO (Either String Template) -getDoc f = do - bs <- catch (liftM Right $ B.readFile f) - (\(e::SomeException) -> return $ Left $ show e) - let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" - return $ (mapRight X.getChildren . - mapLeft genErrorMsg . - X.parse' heistExpatOptions . wrap) =<< bs - where - genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str - locMsg (X.XMLParseLocation line col _ _) = - "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" - translate "junk after document element" = "document must have a single root element" - translate s = s - ------------------------------------------------------------------------------- -mapLeft :: (a -> b) -> Either a c -> Either b c -mapLeft g = either (Left . g) Right -mapRight :: (b -> c) -> Either a b -> Either a c -mapRight g = either Left (Right . g) - - ------------------------------------------------------------------------------- --- | Loads a template with the specified path and filename. The --- template is only loaded if it has a ".tpl" extension. -loadTemplate :: String -> String -> IO [Either String (TPath, Template)] --TemplateMap -loadTemplate path fname - | ".tpl" `isSuffixOf` fname = do - c <- getDoc fname - return [fmap (\t -> (splitPaths $ B.pack tName, t)) c] - | otherwise = return [] - where tName = drop ((length path)+1) $ - take ((length fname) - 4) fname - - ------------------------------------------------------------------------------- --- | Traverses the specified directory structure and builds a --- TemplateState by loading all the files with a ".tpl" extension. -loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) -loadTemplates dir ts = do - d <- readDirectoryWith (loadTemplate dir) dir - let tlist = F.fold (free d) - errs = lefts tlist - case errs of - [] -> liftM Right $ foldM loadHook ts $ rights tlist - _ -> return $ Left $ unlines errs - - ------------------------------------------------------------------------------- --- | Runs the onLoad hook on the template and returns the `TemplateState` --- with the result inserted. -loadHook :: Monad m => TemplateState m -> (TPath, Template) -> IO (TemplateState m) -loadHook ts (tp, t) = do - t' <- _onLoadHook ts t - return $ insertTemplate tp t' ts - - ------------------------------------------------------------------------------- --- These are here until we can get them into hexpat. ------------------------------------------------------------------------------- - -formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> L.ByteString -formatList nodes = foldl L.append L.empty $ map formatNode nodes - -formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> B.ByteString -formatList' = B.concat . L.toChunks . formatList - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index 83efe53..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Apply where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - ------------------------------------------------------------------------------- --- | Default name for the apply splice. -applyTag :: ByteString -applyTag = "apply" - - ------------------------------------------------------------------------------- --- | Default attribute name for the apply tag. -applyAttr :: ByteString -applyAttr = "template" - - ------------------------------------------------------------------------------- --- | Implementation of the apply splice. -applyImpl :: Monad m => Splice m -applyImpl = do - node <- getParamNode - case X.getAttribute node applyAttr of - Nothing -> return [] -- TODO: error handling - Just attr -> do - st <- get - processedChildren <- runNodeList $ X.getChildren node - modify (bindSplice "content" $ return processedChildren) - maybe (return []) -- TODO: error handling - (\(t,ctx) -> do setContext ctx - result <- runNodeList t - put st - return result) - (lookupTemplate attr (st {_curContext = nextCtx attr st})) - where nextCtx name st - | B.isPrefixOf "/" name = [] - | otherwise = _curContext st - - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Bind.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Bind.html deleted file mode 100644 index 27c602f..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Bind.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Bind where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - --- | Default name for the bind splice. -bindTag :: ByteString -bindTag = "bind" - - ------------------------------------------------------------------------------- --- | Default attribute name for the bind tag. -bindAttr :: ByteString -bindAttr = "tag" - - ------------------------------------------------------------------------------- --- | Implementation of the bind splice. -bindImpl :: Monad m => Splice m -bindImpl = do - node <- getParamNode - maybe (return ()) - (add node) - (X.getAttribute node bindAttr) - return [] - - where - add node nm = modify $ bindSplice nm (return $ X.getChildren node) - - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Ignore.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Ignore.html deleted file mode 100644 index 94cfc79..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Ignore.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Ignore where - ------------------------------------------------------------------------------- -import Data.ByteString.Char8 (ByteString) - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | Default name for the ignore splice. -ignoreTag :: ByteString -ignoreTag = "ignore" - - ------------------------------------------------------------------------------- --- | The ignore tag and everything it surrounds disappears in the --- rendered output. -ignoreImpl :: Monad m => Splice m -ignoreImpl = return [] - - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Markdown.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Markdown.html deleted file mode 100644 index 56cc561..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Markdown.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} - -module Text.Templating.Heist.Splices.Markdown where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as BC -import Data.Maybe -import Control.Concurrent -import Control.Exception (evaluate, throwIO) -import Control.Monad -import Control.Monad.CatchIO -import Control.Monad.Trans -import Data.Typeable -import Prelude hiding (catch) -import System.Directory -import System.Exit -import System.IO -import System.Process -import Text.Templating.Heist.Internal -import Text.XML.Expat.Tree hiding (Node) - - -data PandocMissingException = PandocMissingException - deriving (Typeable) - -instance Show PandocMissingException where - show PandocMissingException = - "Cannot find the \"pandoc\" executable; is it on your $PATH?" - -instance Exception PandocMissingException - - -data MarkdownException = MarkdownException ByteString - deriving (Typeable) - -instance Show MarkdownException where - show (MarkdownException e) = - "Markdown error: pandoc replied:\n\n" ++ BC.unpack e - -instance Exception MarkdownException - - ------------------------------------------------------------------------------- --- | Default name for the markdown splice. -markdownTag :: ByteString -markdownTag = "markdown" - ------------------------------------------------------------------------------- --- | Implementation of the markdown splice. -markdownSplice :: MonadIO m => Splice m -markdownSplice = do - pdMD <- liftIO $ findExecutable "pandoc" - - when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException - - tree <- getParamNode - markup <- liftIO $ - case getAttribute tree "file" of - Just f -> pandoc (fromJust pdMD) $ BC.unpack f - Nothing -> pandocBS (fromJust pdMD) $ textContent tree - - let ee = parse' heistExpatOptions markup - case ee of - (Left e) -> throw $ MarkdownException - $ BC.pack ("Error parsing markdown output: " ++ show e) - (Right n) -> return [n] - - -pandoc :: FilePath -> FilePath -> IO ByteString -pandoc pandocPath inputFile = do - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - - -- FIXME: hardcoded path - args = [ "-S", "--no-wrap", "templates/"++inputFile ] - - -pandocBS :: FilePath -> ByteString -> IO ByteString -pandocBS pandocPath s = do - -- using the crummy string functions for convenience here - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - args = [ "-S", "--no-wrap" ] - - --- a version of readProcessWithExitCode that does I/O properly -readProcessWithExitCode' - :: FilePath -- ^ command to run - -> [String] -- ^ any arguments - -> ByteString -- ^ standard input - -> IO (ExitCode,ByteString,ByteString) -- ^ exitcode, stdout, stderr -readProcessWithExitCode' cmd args input = do - (Just inh, Just outh, Just errh, pid) <- - createProcess (proc cmd args){ std_in = CreatePipe, - std_out = CreatePipe, - std_err = CreatePipe } - outMVar <- newEmptyMVar - - outM <- newEmptyMVar - errM <- newEmptyMVar - - -- fork off a thread to start consuming stdout - forkIO $ do - out <- B.hGetContents outh - putMVar outM out - putMVar outMVar () - - -- fork off a thread to start consuming stderr - forkIO $ do - err <- B.hGetContents errh - putMVar errM err - putMVar outMVar () - - -- now write and flush any input - when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh - hClose inh -- done with stdin - - -- wait on the output - takeMVar outMVar - takeMVar outMVar - hClose outh - - -- wait on the process - ex <- waitForProcess pid - - out <- readMVar outM - err <- readMVar errM - - return (ex, out, err) - - - - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Static.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Static.html deleted file mode 100644 index 734781d..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices-Static.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Static - ( StaticTagState - , bindStaticTag - , clearStaticTagCache - ) where - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Monad -import Control.Monad.Trans -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import Data.IORef -import qualified Data.Map as Map -import Data.Map (Map) -import Data.Maybe -import qualified Data.Set as Set -import System.Random -import Text.XML.Expat.Cursor -import Text.XML.Expat.Tree hiding (Node) - - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | State for storing static tag information -newtype StaticTagState = STS (MVar (Map ByteString Template)) - - ------------------------------------------------------------------------------- --- | Clears the static tag state. -clearStaticTagCache :: StaticTagState -> IO () -clearStaticTagCache (STS staticMVar) = - modifyMVar_ staticMVar (const $ return Map.empty) - - ------------------------------------------------------------------------------- --- | The "static" splice ensures that its contents are evaluated once and then --- cached. The cached contents are returned every time the splice is --- referenced. -staticImpl :: (MonadIO m) - => StaticTagState - -> TemplateMonad m Template -staticImpl (STS mv) = do - tree <- getParamNode - let i = fromJust $ getAttribute tree "id" - - mp <- liftIO $ readMVar mv - - (mp',ns) <- do - let mbn = Map.lookup i mp - case mbn of - Nothing -> do - nodes' <- runNodeList $ getChildren tree - return $! (Map.insert i nodes' mp, nodes') - (Just n) -> do - stopRecursion - return $! (mp,n) - - liftIO $ modifyMVar_ mv (const $ return mp') - - return ns - - ------------------------------------------------------------------------------- --- | Modifies a TemplateState to include a "static" tag. -bindStaticTag :: MonadIO m - => TemplateState m - -> IO (TemplateState m, StaticTagState) -bindStaticTag ts = do - sr <- newIORef $ Set.empty - mv <- liftM STS $ newMVar Map.empty - - return $ (addOnLoadHook (assignIds sr) $ - bindSplice "static" (staticImpl mv) ts, - mv) - - where - generateId :: IO Int - generateId = getStdRandom random - - assignIds setref = mapM f - where - f node = g $ fromTree node - - getId = do - i <- liftM (B.pack . show) generateId - st <- readIORef setref - if Set.member i st - then getId - else do - writeIORef setref $ Set.insert i st - return i - - g curs = do - let node = current curs - curs' <- if getName node == "static" - then do - i <- getId - return $ modifyContent (setAttribute "id" i) curs - else return curs - let mbc = nextDF curs' - maybe (return $ toTree curs') g mbc - - - - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices.html deleted file mode 100644 index 9919a2e..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist-Splices.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
module Text.Templating.Heist.Splices - ( module Text.Templating.Heist.Splices.Apply - , module Text.Templating.Heist.Splices.Bind - , module Text.Templating.Heist.Splices.Ignore - , module Text.Templating.Heist.Splices.Markdown - , module Text.Templating.Heist.Splices.Static - ) where - -import Text.Templating.Heist.Splices.Apply -import Text.Templating.Heist.Splices.Bind -import Text.Templating.Heist.Splices.Ignore -import Text.Templating.Heist.Splices.Markdown -import Text.Templating.Heist.Splices.Static - -- diff --git a/static/docs/0.1.2/heist/src/Text-Templating-Heist.html b/static/docs/0.1.2/heist/src/Text-Templating-Heist.html deleted file mode 100644 index e38d59b..0000000 --- a/static/docs/0.1.2/heist/src/Text-Templating-Heist.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} - -{-| - - This module contains the core definitions for the Heist template system. - - The Heist template system is based on XML\/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - - The most important concept in Heist is the 'Splice'. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. 'Splice' is implemented as a type synonym @type Splice m = - TemplateMonad m [Node]@, and 'TemplateMonad' has a function 'getParamNode' - that lets you get the input node. - - Suppose you have a place on your page where you want to display a link with - the text \"Logout username\" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - @getUser :: MyAppMonad (Maybe ByteString)@ that gets the current user. - You can implement this functionality with a 'Splice' as follows: - - > - > import Text.XML.Expat.Tree - > - > link :: ByteString -> ByteString -> Node - > link target text = X.Element "a" [("href", target)] [X.Text text] - > - > loginLink :: Node - > loginLink = link "/login" "Login" - > - > logoutLink :: ByteString -> Node - > logoutLink user = link "/logout" (B.append "Logout " user) - > - > loginLogoutSplice :: Splice MyAppMonad - > loginLogoutSplice = do - > user <- lift getUser - > return $ [maybe loginLink logoutLink user] - > - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the 'TemplateState' data structure. The - following code demonstrates how this splice would be used. - - > mySplices = [ ("loginLogout", loginLogoutSplice) ] - > - > main = do - > ets <- loadTemplates "templates" $ - > foldr (uncurry bindSplice) emptyTemplateState mySplices - > let ts = either error id ets - > t <- runMyAppMonad $ renderTemplate ts "index" - > print $ maybe "Page not found" id t - - Here we build up our 'TemplateState' by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final 'TemplateState' wrapped in an Either to handle - errors. Then we use this 'TemplateState' to render our templates. - --} - -module Text.Templating.Heist - ( - -- * Types - Node - , Splice - , Template - , TemplateMonad - , TemplateState - - -- * Functions and declarations on TemplateState values - , addTemplate - , emptyTemplateState - , bindSplice - , lookupSplice - , setTemplates - , loadTemplates - - -- * Hook functions - -- $hookDoc - , addOnLoadHook - , addPreRunHook - , addPostRunHook - - -- * TemplateMonad functions - , stopRecursion - , getParamNode - , runNodeList - , getContext - - -- * Functions for running splices and templates - , runTemplate - , evalTemplate - , callTemplate - , renderTemplate - , bindStrings - - -- * Misc functions - , runSplice - , runRawTemplate - , getDoc - , bindStaticTag - - , heistExpatOptions - , module Text.Templating.Heist.Constants - ) where - -import Control.Monad.Trans -import qualified Data.Map as Map -import Text.Templating.Heist.Internal -import Text.Templating.Heist.Constants -import Text.Templating.Heist.Splices - - ------------------------------------------------------------------------------- --- | The default set of built-in splices. -defaultSpliceMap :: MonadIO m => SpliceMap m -defaultSpliceMap = Map.fromList - [(applyTag, applyImpl) - ,(bindTag, bindImpl) - ,(ignoreTag, ignoreImpl) - ,(markdownTag, markdownSplice) - ] - - ------------------------------------------------------------------------------- --- | An empty template state, with Heist's default splices (@\<bind\>@ and --- @\<apply\>@) mapped. -emptyTemplateState :: MonadIO m => TemplateState m -emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 - return return return - - ------------------------------------------------------------------------------- --- | Reloads the templates from disk and renders the specified --- template. (Old convenience code.) ---renderTemplate' :: FilePath -> ByteString -> IO (Maybe ByteString) ---renderTemplate' baseDir name = do --- etm <- loadTemplates baseDir emptyTemplateState --- let ts = either (const emptyTemplateState) id etm --- ns <- runTemplate ts name --- return $ (Just . formatList') =<< ns - - --- $hookDoc --- Heist hooks allow you to modify templates when they are loaded and before --- and after they are run. Every time you call one of the addAbcHook --- functions the hook is added to onto the processing pipeline. The hooks --- processes the template in the order that they were added to the --- TemplateState. --- --- The pre-run and post-run hooks are run before and after every template is --- run/rendered. You should be careful what code you put in these hooks --- because it can significantly affect the performance of your site. - -- diff --git a/static/docs/0.1.2/heist/src/hscolour.css b/static/docs/0.1.2/heist/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.2/heist/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.1.2/snap-core/Data-CIByteString.html b/static/docs/0.1.2/snap-core/Data-CIByteString.html deleted file mode 100644 index 01d3740..0000000 --- a/static/docs/0.1.2/snap-core/Data-CIByteString.html +++ /dev/null @@ -1,324 +0,0 @@ - - -
| |||||
| |||||
Description | |||||
Data.CIByteString is a module containing CIByteString, a wrapper for - ByteString which provides case-insensitive (ASCII-wise) Ord and Eq - instances. - CIByteString also has an IsString instance, so if you use the - "OverloadedStrings" LANGUAGE pragma you can write case-insensitive string - literals, e.g.: - - > let a = "Foo" in - putStrLn $ (show $ unCI a) ++ "==\"FoO\" is " ++ show (a == "FoO") - "Foo"=="FoO" is True - | |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for (optionally) printing debugging - messages. Normally debug does nothing, but you can pass "-fdebug" to - cabal install to build a snap-core which debugs to stderr. - N.B. this is an internal interface, please don't write external code that - depends on it. - | |||||
Documentation | |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An internal Snap module containing HTTP types. - N.B. this is an internal interface, please don't write user code that - depends on it. Most of these declarations (except for the - unsafe/encapsulation-breaking ones) are re-exported from Snap.Types. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Documentation | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets all of the values for a given header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for debugging iteratees. - N.B. this is an internal interface, please don't write user code that - depends on it. - | |||||
Documentation | |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||
Snap Framework type aliases and utilities for iteratees. Note that as a - convenience, this module also exports everything from Data.Iteratee in the - iteratee library. - WARNING: Note that all of these types are scheduled to change in the - darcs head version of the iteratee library; John Lato et al. are working - on a much improved iteratee formulation. - | |||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Convenience aliases around types from Data.Iteratee - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Re-export types and functions from Data.Iteratee - | |||||||||||||||||||||||||||
module Data.Iteratee | |||||||||||||||||||||||||||
Helper functions - | |||||||||||||||||||||||||||
Enumerators - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a strict bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Conversion to/from WrappedByteString - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a wrapped bytestring to a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a lazy bytestring to a wrapped bytestring. - | |||||||||||||||||||||||||||
Iteratee utilities - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads n elements from a stream and applies the given iteratee to - the stream of the read elements. Reads exactly n elements, and if - the stream is short propagates an error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads up to n elements from a stream and applies the given iteratee to the - stream of the read elements. If more than n elements are read, propagates an - error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Wraps an Iteratee, counting the number of bytes consumed by it. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Buffers an iteratee. - Our enumerators produce a lot of little strings; rather than spending all - our time doing kernel context switches for 4-byte write() calls, we buffer - the iteratee to send 2KB at a time. - | |||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core type definitions, class instances, and functions -for HTTP as well as the Snap monad, which is used for web handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Snap Monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action in the 'Iteratee IO' monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for control flow and early termination - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Short-circuits a Snap monad action early, storing the given - Response value in its state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fails out of a Snap monad action. This is used to indicate - that you choose not to handle the given request within the given - handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Routing - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only if the request's HTTP method matches - the given method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only when rqPathInfo is empty. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A web handler which, given a mapping from URL entry points to web - handlers, efficiently routes requests to the correct handler. - The URL entry points are given as relative paths, for example: - route [ ("foo/bar/quux", fooBarQuux) ] - If the URI of the incoming request is - /foo/bar/quux - or - /foo/bar/quux/...anything... - then the request will be routed to "fooBarQuux", with rqContextPath - set to "/foo/bar/quux/" and rqPathInfo set to - "...anything...". - FIXME/TODO: we need a version with and without the context path setting - behaviour; if the route is "article/:id/print", we probably want the - contextPath to be "/article" instead of "/article/whatever/print". - A path component within an URL entry point beginning with a colon (":") - is treated as a variable capture; the corresponding path component within - the request URI will be entered into the rqParams parameters mapping with - the given name. For instance, if the routes were: - route [ ("foo/:bar/baz", fooBazHandler) ] - Then a request for "/foo/saskatchewan/baz" would be routed to - fooBazHandler with a mapping for: - "bar" => "saskatchewan" - in its parameters table. - Longer paths are matched first, and specific routes are matched before - captures. That is, if given routes: - [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] - a request for "/a/b" will go to h2, "/a/s" for any s will go - to h3, and "/a" will go to h1. - The following example matches "/article" to an article index, - "/login" to a login, and "/article/..." to an article renderer. - route [ ("article", renderIndex) - , ("article/:id", renderArticle) - , ("login", method POST doLogin) ] - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The routeLocal function is the same as route, except it doesn't change - the request's context path. This is useful if you want to route to a - particular handler but you want that handler to receive the rqPathInfo as - it is. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Access to state - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Request object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Response object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Request object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Response object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the Request object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifes the Response object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap action with a locally-modified Request state - object. The Request object in the Snap monad state after the call - to localRequest will be unchanged. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Request from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Response from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabbing request bodies - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sends the request body through an iteratee (data consumer) and - returns the result. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the request body as a bytestring. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Detaches the request body's Enumerator from the Request and - returns it. You would want to use this if you needed to send the - HTTP request body (transformed or otherwise) through to the output - in O(1) space. (Examples: transcoding, "echo", etc) - Normally Snap is careful to ensure that the request body is fully - consumed after your web handler runs; this function is marked - "unsafe" because it breaks this guarantee and leaves the - responsibility up to you. If you don't fully consume the - Enumerator you get here, the next HTTP request in the pipeline - (if any) will misparse. Be careful with exception handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP Datatypes and Functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP-related datatypes: Request, Response, Cookie, etc. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Headers - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Requests - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The server name of the request, as it came in from the request's - Host: header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the port number the HTTP server is listening on. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote IP address. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote TCP port number. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The local IP address for this request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP server's idea of its local hostname. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns True if this is an HTTPS session (currently always - False). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Content-Length of the HTTP request body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP request method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP version used by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns a list of the cookies that came in from the HTTP request - headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Handlers can (will be; --ed) be hung on a URI "entry point"; - this is called the "context path". If a handler is hung on the - context path "/foo/", and you request "/foo/bar", the value - of rqPathInfo will be "bar". - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The "context path" of the request; catenating rqContextPath, and - rqPathInfo should get you back to the original rqURI. The - rqContextPath always begins and ends with a slash ("/") - character, and represents the path (relative to your - component/snaplet) you took to get to your handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the URI requested by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP query string for this Request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Params mapping for this Request. "Parameters" are - automatically decoded from the query string and POST body and - entered into this mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Responses - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status code. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status explanation string. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Response I/O - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the output to be the contents of the specified file. - Calling sendFile will overwrite any output queued to be sent in the - Response. If the response body is not modified after the call to - sendFile, Snap will use the efficient sendfile() system call on - platforms that support it. - If the response body is modified (using modifyResponseBody), the file will - be read using mmap(). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Iteratee - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP utilities - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Description | |||||||||||||
Contains web handlers to serve files from a directory. - | |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
Gets a path from the Request using rqPathInfo and makes sure it is - safe to use for opening files. A path is safe if it is a relative path - and has no .. elements to escape the intended directory structure. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
The default set of mime type mappings we use when serving files. Its - value: - Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - | |||||||||||||
| |||||||||||||
A type alias for MIME type - | |||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||
| ||||||||
Synopsis | ||||||||
| ||||||||
Documentation | ||||||||
| ||||||||
| ||||||||
| ||||||||
| ||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (>) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (A) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (B) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||
Index (C) | ||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (D) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
Index (E) | ||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||
Index (F) | ||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (G) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (H) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (I) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (J) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (L) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (M) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (N) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (O) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||
| ||||||||||||||||||||||||
Index (P) | ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Index (R) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||
Index (S) | ||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (T) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (U) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (W) | |||||||||||||||||||||
|
| |||||||||||||||||||||
Index | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
snap-core-0.1.2: Snap: A Haskell Web Framework (Core) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - This library contains the core definitions and types for the Snap framework, -including: - 1. Primitive types and functions for HTTP (requests, responses, cookies, -post/query parameters, etc) - 2. Type aliases and helper functions for Iteratee I/O - 3. A monad for programming web handlers called "Snap", inspired by -happstack's (http://happstack.com/index.html), which allows: -
Quick start: The Snap monad and HTTP definitions are in Snap.Types, -some iteratee utilities are in Snap.Iteratee. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - --- | "Data.CIByteString" is a module containing 'CIByteString', a wrapper for --- 'ByteString' which provides case-insensitive (ASCII-wise) 'Ord' and 'Eq' --- instances. --- --- 'CIByteString' also has an 'IsString' instance, so if you use the --- \"OverloadedStrings\" LANGUAGE pragma you can write case-insensitive string --- literals, e.g.: --- --- @ --- \> let a = \"Foo\" in --- putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++ show (a == \"FoO\") --- \"Foo\"==\"FoO\" is True --- @ - -module Data.CIByteString - ( CIByteString - , toCI - , unCI - ) where - --- for IsString instance -import Data.ByteString.Char8 () -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString as S -import Data.Char -import Data.String - - --- | A case-insensitive newtype wrapper for 'ByteString' -data CIByteString = CIByteString { unCI :: !ByteString - , _lowercased :: !ByteString } - -toCI :: ByteString -> CIByteString -toCI s = CIByteString s t - where - t = lowercase s - -instance Show CIByteString where - show (CIByteString s _) = show s - -lowercase :: ByteString -> ByteString -lowercase = S.map (c2w . toLower . w2c) - -instance Eq CIByteString where - (CIByteString _ a) == (CIByteString _ b) = a == b - (CIByteString _ a) /= (CIByteString _ b) = a /= b - -instance Ord CIByteString where - (CIByteString _ a) <= (CIByteString _ b) = a <= b - -instance IsString CIByteString where - fromString = toCI . fromString -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Internal-Debug.html b/static/docs/0.1.2/snap-core/src/Snap-Internal-Debug.html deleted file mode 100644 index c295589..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Internal-Debug.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - -
-- | An internal Snap module for (optionally) printing debugging --- messages. Normally 'debug' does nothing, but you can pass \"-fdebug\" to --- @cabal install@ to build a @snap-core@ which debugs to stderr. --- --- /N.B./ this is an internal interface, please don't write external code that --- depends on it. - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} - -module Snap.Internal.Debug where - -import Control.Monad.Trans - -#ifdef DEBUG_TEST - -debug :: (MonadIO m) => String -> m () -debug !s = return $ s `seq` () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno !s = return $ s `seq` () - -#elif defined(DEBUG) - ------------------------------------------------------------------------------- -import Control.Concurrent -import Data.List -import Data.Maybe -import Foreign.C.Error -import System.IO -import System.IO.Unsafe -import Text.Printf ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -_debugMVar :: MVar () -_debugMVar = unsafePerformIO $ newMVar () - - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug s = liftIO $ withMVar _debugMVar $ \_ -> do - tid <- myThreadId - hPutStrLn stderr $ s' tid - hFlush stderr - where - chop x = let y = fromMaybe x $ stripPrefix "ThreadId " x - in printf "%8s" y - - s' t = "[" ++ chop (show t) ++ "] " ++ s - -{-# INLINE debug #-} - - ------------------------------------------------------------------------------- -debugErrno :: (MonadIO m) => String -> m () -debugErrno loc = liftIO $ do - err <- getErrno - let ex = errnoToIOError loc err Nothing Nothing - debug $ show ex ------------------------------------------------------------------------------- - -#else - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug _ = return () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno _ = return () ------------------------------------------------------------------------------- - -#endif -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Internal-Http-Types.html b/static/docs/0.1.2/snap-core/src/Snap-Internal-Http-Types.html deleted file mode 100644 index 44d3120..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Internal-Http-Types.html +++ /dev/null @@ -1,648 +0,0 @@ - - - - -
-- | An internal Snap module containing HTTP types. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. Most of these declarations (except for the --- unsafe/encapsulation-breaking ones) are re-exported from "Snap.Types". - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeSynonymInstances #-} - -module Snap.Internal.Http.Types where - - ------------------------------------------------------------------------------- -import Control.Applicative hiding (empty) -import Control.Monad (liftM, when) -import qualified Data.Attoparsec as Atto -import Data.Attoparsec hiding (many, Result(..)) -import Data.Bits -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w,w2c) -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import qualified Data.ByteString as S -import Data.Char -import Data.DList (DList) -import qualified Data.DList as DL -import Data.IORef -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Monoid -import Data.Serialize.Builder -import Data.Time.Clock -import Data.Time.Format -import Data.Word -import Foreign hiding (new) -import Foreign.C.String -import Foreign.C.Types -import Prelude hiding (take) -import System.Locale (defaultTimeLocale) - ------------------------------------------------------------------------------- -import Data.CIByteString -import qualified Snap.Iteratee as I - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "set_c_locale" - set_c_locale :: IO () - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_parse_http_time" - c_parse_http_time :: CString -> IO CTime - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_format_http_time" - c_format_http_time :: CTime -> CString -> IO () - ------------------------------------------------------------------------------- -type Enumerator a = I.Enumerator IO a - ------------------------------------------------------------------------------- --- | A type alias for a case-insensitive key-value mapping. -type Headers = Map CIByteString [ByteString] - - ------------------------------------------------------------------------------- --- | A typeclass for datatypes which contain HTTP headers. -class HasHeaders a where - - -- | Modify the datatype's headers. - updateHeaders :: (Headers -> Headers) -> a -> a - - -- | Retrieve the headers from a datatype that has headers. - headers :: a -> Headers - - ------------------------------------------------------------------------------- --- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header with --- the same name already exists, the new value is appended to the headers list. -addHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -addHeader k v = updateHeaders $ Map.insertWith' (++) k [v] - - ------------------------------------------------------------------------------- --- | Sets a header key-value-pair in a 'HasHeaders' datatype. If a header with --- the same name already exists, it is overwritten with the new value. -setHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -setHeader k v = updateHeaders $ Map.insert k [v] - - ------------------------------------------------------------------------------- --- | Gets all of the values for a given header. -getHeaders :: (HasHeaders a) => CIByteString -> a -> Maybe [ByteString] -getHeaders k a = Map.lookup k $ headers a - - ------------------------------------------------------------------------------- --- | Gets a header value out of a 'HasHeaders' datatype. If many headers came --- in with the same name, they will be catenated together. -getHeader :: (HasHeaders a) => CIByteString -> a -> Maybe ByteString -getHeader k a = liftM (S.intercalate " ") (Map.lookup k $ headers a) - - ------------------------------------------------------------------------------- --- | Enumerates the HTTP method values (see --- <http://tools.ietf.org/html/rfc2068.html#section-5.1.1>). -data Method = GET | HEAD | POST | PUT | DELETE | TRACE | OPTIONS | CONNECT - deriving(Show,Read,Ord,Eq) - - ------------------------------------------------------------------------------- -type HttpVersion = (Int,Int) - - ------------------------------------------------------------------------------- --- | A datatype representing an HTTP cookie. -data Cookie = Cookie { - -- | The name of the cookie. - cookieName :: !ByteString - - -- | The cookie's string value. - , cookieValue :: !ByteString - - -- | The cookie's expiration value, if it has one. - , cookieExpires :: !(Maybe UTCTime) - - -- | The cookie's \"domain\" value, if it has one. - , cookieDomain :: !(Maybe ByteString) - - -- | The cookie path. - , cookiePath :: !(Maybe ByteString) -} deriving (Eq, Show) - - ------------------------------------------------------------------------------- --- | A type alias for the HTTP parameters mapping. Each parameter --- key maps to a list of ByteString values; if a parameter is specified --- multiple times (e.g.: \"@GET /foo?param=bar1¶m=bar2@\"), looking up --- \"@param@\" in the mapping will give you @[\"bar1\", \"bar2\"]@. -type Params = Map ByteString [ByteString] - - ------------------------------------------------------------------------------- --- request type ------------------------------------------------------------------------------- - -data SomeEnumerator = SomeEnumerator (forall a . Enumerator a) - - ------------------------------------------------------------------------------- --- | Contains all of the information about an incoming HTTP request. -data Request = Request - { -- | The server name of the request, as it came in from the request's - -- @Host:@ header. - rqServerName :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqServerPort :: !Int - - -- | The remote IP address. - , rqRemoteAddr :: !ByteString - - -- | The remote TCP port number. - , rqRemotePort :: !Int - - -- | The local IP address for this request. - , rqLocalAddr :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqLocalPort :: !Int - - -- | Returns the HTTP server's idea of its local hostname. - , rqLocalHostname :: !ByteString - - -- | Returns @True@ if this is an @HTTPS@ session (currently always - -- @False@). - , rqIsSecure :: !Bool - , rqHeaders :: Headers - , rqBody :: IORef SomeEnumerator - - -- | Returns the @Content-Length@ of the HTTP request body. - , rqContentLength :: !(Maybe Int) - - -- | Returns the HTTP request method. - , rqMethod :: !Method - - -- | Returns the HTTP version used by the client. - , rqVersion :: !HttpVersion - - -- | Returns a list of the cookies that came in from the HTTP request - -- headers. - , rqCookies :: [Cookie] - - - -- | We'll be doing web components (or \"snaplets\") for version 0.2. The - -- \"snaplet path\" refers to the place on the URL where your containing - -- snaplet is hung. The value of 'rqSnapletPath' is either @\"\"@ (at the - -- top-level context) or is a path beginning with a slash, but not ending - -- with one. - -- - -- An identity is that: - -- - -- > rqURI r == 'S.concat' [ rqSnapletPath r - -- > , rqContextPath r - -- > , rqPathInfo r ] - -- - -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will be - -- \"\" - , rqSnapletPath :: !ByteString - - -- | Handlers can (/will be; --ed/) be hung on a @URI@ \"entry point\"; - -- this is called the \"context path\". If a handler is hung on the - -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the value - -- of 'rqPathInfo' will be @\"bar\"@. - , rqPathInfo :: !ByteString - - -- | The \"context path\" of the request; catenating 'rqContextPath', and - -- 'rqPathInfo' should get you back to the original 'rqURI'. The - -- 'rqContextPath' always begins and ends with a slash (@\"\/\"@) - -- character, and represents the path (relative to your - -- component\/snaplet) you took to get to your handler. - , rqContextPath :: !ByteString - - -- | Returns the @URI@ requested by the client. - , rqURI :: !ByteString - - -- | Returns the HTTP query string for this 'Request'. - , rqQueryString :: !ByteString - - -- | Returns the 'Params' mapping for this 'Request'. \"Parameters\" are - -- automatically decoded from the query string and @POST@ body and - -- entered into this mapping. - , rqParams :: Params - } - - ------------------------------------------------------------------------------- -instance Show Request where - show r = concat [ "Request <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - sname - , remote - , local - , beginheaders - , hdrs - , endheaders - , contentlength - , method - , version - , cookies - , pathinfo - , contextpath - , snapletpath - , uri - , params - ] - - sname = concat [ "server-name: ", toStr $ rqServerName r ] - remote = concat [ "remote: " - , toStr $ rqRemoteAddr r - , ":" - , show (rqRemotePort r) - ] - local = concat [ "local: " - , toStr $ rqLocalAddr r - , ":" - , show $ rqServerPort r - ] - beginheaders = "Headers:\n ========================================" - endheaders = " ========================================" - hdrs = " " ++ show (rqHeaders r) - contentlength = concat [ "content-length: " - , show $ rqContentLength r - ] - method = concat [ "method: " - , show $ rqMethod r - ] - version = concat [ "version: " - , show $ rqVersion r - ] - cookies = concat [ "cookies:\n" - , " ========================================\n" - , " " ++ (show $ rqCookies r) - , "\n ========================================" - ] - pathinfo = concat [ "pathinfo: ", toStr $ rqPathInfo r ] - contextpath = concat [ "contextpath: ", toStr $ rqContextPath r ] - snapletpath = concat [ "snapletpath: ", toStr $ rqSnapletPath r ] - uri = concat [ "URI: ", toStr $ rqURI r ] - params = concat [ "params:\n" - , " ========================================\n" - , " " ++ (show $ rqParams r) - , "\n ========================================" - ] - - ------------------------------------------------------------------------------- -instance HasHeaders Request where - headers = rqHeaders - updateHeaders f r = r { rqHeaders = f (rqHeaders r) } - - ------------------------------------------------------------------------------- -instance HasHeaders Headers where - headers = id - updateHeaders = id - ------------------------------------------------------------------------------- --- response type ------------------------------------------------------------------------------- - -data ResponseBody = Enum (forall a . Enumerator a) -- ^ output body is enumerator - | SendFile FilePath -- ^ output body is sendfile() - - ------------------------------------------------------------------------------- -rspBodyMap :: (forall a . Enumerator a -> Enumerator a) - -> ResponseBody - -> ResponseBody -rspBodyMap f b = Enum $ f $ rspBodyToEnum b - - ------------------------------------------------------------------------------- -rspBodyToEnum :: ResponseBody -> Enumerator a -rspBodyToEnum (Enum e) = e -rspBodyToEnum (SendFile fp) = I.enumFile fp - - ------------------------------------------------------------------------------- --- | Represents an HTTP response. -data Response = Response - { rspHeaders :: Headers - , rspHttpVersion :: !HttpVersion - - -- | We will need to inspect the content length no matter what, and - -- looking up \"content-length\" in the headers and parsing the number - -- out of the text will be too expensive. - , rspContentLength :: !(Maybe Int) - , rspBody :: ResponseBody - - -- | Returns the HTTP status code. - , rspStatus :: !Int - - -- | Returns the HTTP status explanation string. - , rspStatusReason :: !ByteString - } - - ------------------------------------------------------------------------------- -instance Show Response where - show r = concat [ "Response <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - hdrs - , version - , status - , reason - ] - - hdrs = concat [ "headers:\n" - , " ==============================\n " - , show $ rspHeaders r - , "\n ==============================" ] - - version = concat [ "version: ", show $ rspHttpVersion r ] - status = concat [ "status: ", show $ rspStatus r ] - reason = concat [ "reason: ", toStr $ rspStatusReason r ] - - ------------------------------------------------------------------------------- -instance HasHeaders Response where - headers = rspHeaders - updateHeaders f r = r { rspHeaders = f (rspHeaders r) } - - ------------------------------------------------------------------------------- --- | Looks up the value(s) for the given named parameter. Parameters initially --- come from the request's query string and any decoded POST body (if the --- request's @Content-Type@ is @application\/x-www-form-urlencoded@). Parameter --- values can be modified within handlers using "rqModifyParams". -rqParam :: ByteString -- ^ parameter name to look up - -> Request -- ^ HTTP request - -> Maybe [ByteString] -rqParam k rq = Map.lookup k $ rqParams rq -{-# INLINE rqParam #-} - - ------------------------------------------------------------------------------- --- | Modifies the parameters mapping (which is a @Map ByteString ByteString@) in --- a 'Request' using the given function. -rqModifyParams :: (Params -> Params) -> Request -> Request -rqModifyParams f r = r { rqParams = p } - where - p = f $ rqParams r -{-# INLINE rqModifyParams #-} - - ------------------------------------------------------------------------------- --- | Writes a key-value pair to the parameters mapping within the given request. -rqSetParam :: ByteString -- ^ parameter name - -> [ByteString] -- ^ parameter values - -> Request -- ^ request - -> Request -rqSetParam k v = rqModifyParams $ Map.insert k v -{-# INLINE rqSetParam #-} - ------------------------------------------------------------------------------- --- responses ------------------------------------------------------------------------------- - --- | An empty 'Response'. -emptyResponse :: Response -emptyResponse = Response Map.empty (1,1) Nothing (Enum return) 200 "OK" - - ------------------------------------------------------------------------------- --- | Sets an HTTP response body to the given 'Enumerator' value. -setResponseBody :: (forall a . Enumerator a) -- ^ new response body - -- enumerator - -> Response -- ^ response to modify - -> Response -setResponseBody e r = r { rspBody = Enum e } -{-# INLINE setResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the HTTP response status. -setResponseStatus :: Int -- ^ HTTP response integer code - -> ByteString -- ^ HTTP response explanation - -> Response -- ^ Response to be modified - -> Response -setResponseStatus s reason r = r { rspStatus=s, rspStatusReason=reason } -{-# INLINE setResponseStatus #-} - - ------------------------------------------------------------------------------- --- | Modifies a response body. -modifyResponseBody :: (forall a . Enumerator a -> Enumerator a) - -> Response - -> Response -modifyResponseBody f r = r { rspBody = rspBodyMap f (rspBody r) } -{-# INLINE modifyResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the @Content-Type@ in the 'Response' headers. -setContentType :: ByteString -> Response -> Response -setContentType = setHeader "Content-Type" -{-# INLINE setContentType #-} - - ------------------------------------------------------------------------------- --- | Adds an HTTP 'Cookie' to the 'Response' headers. -addCookie :: Cookie -- ^ cookie value - -> Response -- ^ response to modify - -> Response -addCookie (Cookie k v mbExpTime mbDomain mbPath) = updateHeaders f - where - f = Map.insertWith' (++) "Set-Cookie" [cookie] - cookie = S.concat [k, "=", v, path, exptime, domain] - path = maybe "" (S.append "; path=") mbPath - domain = maybe "" (S.append "; domain=") mbDomain - exptime = maybe "" (S.append "; expires=" . fmt) mbExpTime - fmt = fromStr . formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT" - - ------------------------------------------------------------------------------- --- | A note here: if you want to set the @Content-Length@ for the response, --- Snap forces you to do it with this function rather than by setting it in the --- headers; the @Content-Length@ in the headers will be ignored. --- --- The reason for this is that Snap needs to look up the value of --- @Content-Length@ for each request, and looking the string value up in the --- headers and parsing the number out of the text will be too expensive. --- --- If you don't set a content length in your response, HTTP keep-alive will be --- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For HTTP\/1.1 --- clients, Snap will switch to the chunked transfer encoding if --- @Content-Length@ is not specified. -setContentLength :: Int -> Response -> Response -setContentLength l r = r { rspContentLength = Just l } -{-# INLINE setContentLength #-} - - ------------------------------------------------------------------------------- --- | Removes any @Content-Length@ set in the 'Response'. -clearContentLength :: Response -> Response -clearContentLength r = r { rspContentLength = Nothing } -{-# INLINE clearContentLength #-} - - ------------------------------------------------------------------------------- --- HTTP dates - -{- --- | Converts a 'ClockTime' into an HTTP timestamp. -formatHttpTime :: UTCTime -> ByteString -formatHttpTime = fromStr . formatTime defaultTimeLocale "%a, %d %b %Y %X GMT" - --- | Converts an HTTP timestamp into a 'UTCTime'. -parseHttpTime :: ByteString -> Maybe UTCTime -parseHttpTime s' = - parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" s - where - s = toStr s' --} - --- | Converts a 'CTime' into an HTTP timestamp. -formatHttpTime :: CTime -> IO ByteString -formatHttpTime t = allocaBytes 40 $ \ptr -> do - c_format_http_time t ptr - S.packCString ptr - - ------------------------------------------------------------------------------- --- | Converts an HTTP timestamp into a 'CTime'. -parseHttpTime :: ByteString -> IO CTime -parseHttpTime s = S.useAsCString s $ \ptr -> - c_parse_http_time ptr - - ------------------------------------------------------------------------------- --- URL ENCODING ------------------------------------------------------------------------------- - -parseToCompletion :: Parser a -> ByteString -> Maybe a -parseToCompletion p s = toResult $ finish r - where - r = parse p s - - toResult (Atto.Done _ c) = Just c - toResult _ = Nothing - - ------------------------------------------------------------------------------- -pUrlEscaped :: Parser ByteString -pUrlEscaped = do - sq <- nextChunk DL.empty - return $ S.concat $ DL.toList sq - - where - nextChunk :: DList ByteString -> Parser (DList ByteString) - nextChunk s = (endOfInput *> pure s) <|> do - c <- anyWord8 - case w2c c of - '+' -> plusSpace s - '%' -> percentEncoded s - _ -> unEncoded c s - - percentEncoded :: DList ByteString -> Parser (DList ByteString) - percentEncoded l = do - hx <- take 2 - when (S.length hx /= 2 || - (not $ S.all (isHexDigit . w2c) hx)) $ - fail "bad hex in url" - - let code = (Cvt.hex hx) :: Word8 - nextChunk $ DL.snoc l (S.singleton code) - - unEncoded :: Word8 -> DList ByteString -> Parser (DList ByteString) - unEncoded c l' = do - let l = DL.snoc l' (S.singleton c) - bs <- takeTill (flip elem (map c2w "%+")) - if S.null bs - then nextChunk l - else nextChunk $ DL.snoc l bs - - plusSpace :: DList ByteString -> Parser (DList ByteString) - plusSpace l = nextChunk (DL.snoc l (S.singleton $ c2w ' ')) - - ------------------------------------------------------------------------------- --- | Decodes an URL-escaped string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlDecode :: ByteString -> Maybe ByteString -urlDecode = parseToCompletion pUrlEscaped - - ------------------------------------------------------------------------------- --- "...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," --- [not including the quotes - ed], and reserved characters used for their --- reserved purposes may be used unencoded within a URL." - --- | URL-escapes a string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlEncode :: ByteString -> ByteString -urlEncode = toByteString . S.foldl' f empty - where - f b c = - if c == c2w ' ' - then b `mappend` singleton (c2w '+') - else if isKosher c - then b `mappend` singleton c - else b `mappend` hexd c - - isKosher w = any ($ c) [ isAlphaNum - , flip elem ['$', '-', '.', '!', '*' - , '\'', '(', ')', ',' ]] - where - c = w2c w - - ------------------------------------------------------------------------------- -hexd :: Word8 -> Builder -hexd c = singleton (c2w '%') `mappend` singleton hi `mappend` singleton low - where - d = c2w . intToDigit - low = d $ fromEnum $ c .&. 0xf - hi = d $ fromEnum $ (c .&. 0xf0) `shift` (-4) - - ------------------------------------------------------------------------------- -finish :: Atto.Result a -> Atto.Result a -finish (Atto.Partial f) = flip feed "" $ f "" -finish x = x - - ------------------------------------------------------------------------------- --- local definitions -fromStr :: String -> ByteString -fromStr = S.pack . map c2w -{-# INLINE fromStr #-} - ------------------------------------------------------------------------------- --- private helper functions -toStr :: ByteString -> String -toStr = map w2c . S.unpack - -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Internal-Iteratee-Debug.html b/static/docs/0.1.2/snap-core/src/Snap-Internal-Iteratee-Debug.html deleted file mode 100644 index 972fc9d..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Internal-Iteratee-Debug.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
-- | An internal Snap module for debugging iteratees. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. - -{-# OPTIONS_GHC -fno-warn-orphans #-} -{-# LANGUAGE FlexibleInstances #-} - -module Snap.Internal.Iteratee.Debug ( debugIteratee ) where - ------------------------------------------------------------------------------- -import Data.Iteratee.WrappedByteString -import Data.Word (Word8) -import System.IO ------------------------------------------------------------------------------- -import Snap.Iteratee ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -instance Show (WrappedByteString Word8) where - show (WrapBS s) = show s - - ------------------------------------------------------------------------------- -debugIteratee :: Iteratee IO () -debugIteratee = IterateeG f - where - f c@(EOF _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return (Done () c) - - f c@(Chunk _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return $ Cont debugIteratee Nothing -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Internal-Routing.html b/static/docs/0.1.2/snap-core/src/Snap-Internal-Routing.html deleted file mode 100644 index ab4539e..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Internal-Routing.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - -
module Snap.Internal.Routing where - - ------------------------------------------------------------------------------- -import Control.Applicative ((<|>)) -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.Monoid -import qualified Data.Map as Map - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Types - - ------------------------------------------------------------------------------- -{-| - -The internal data type you use to build a routing tree. Matching is -done unambiguously. - -'Capture' and 'Dir' routes can have a "fallback" route: - - - For 'Capture', the fallback is routed when there is nothing to capture - - For 'Dir', the fallback is routed when we can't find a route in its map - -Fallback routes are stacked: i.e. for a route like: - -> Dir [("foo", Capture "bar" (Action bar) NoRoute)] baz - -visiting the URI foo/ will result in the "bar" capture being empty and -triggering its fallback. It's NoRoute, so we go to the nearest parent -fallback and try that, which is the baz action. - --} -data Route a = Action (Snap a) -- wraps a 'Snap' action - | Capture ByteString (Route a) (Route a) -- captures the dir in a param - | Dir (Map.Map ByteString (Route a)) (Route a) -- match on a dir - | NoRoute - - ------------------------------------------------------------------------------- -instance Monoid (Route a) where - mempty = NoRoute - - -- Unions two routes, favoring the right-hand side - mappend NoRoute r = r - - mappend l@(Action _) r = case r of - (Action _) -> r - (Capture p r' fb) -> Capture p r' (mappend fb l) - (Dir _ _) -> mappend (Dir Map.empty l) r - NoRoute -> l - - mappend l@(Capture p r' fb) r = case r of - (Action _) -> Capture p r' (mappend fb r) - (Capture p' r'' fb') - | p == p' -> Capture p (mappend r' r'') (mappend fb fb') - | otherwise -> r - (Dir rm fb') -> Dir rm (mappend fb' l) - NoRoute -> l - - mappend l@(Dir rm fb) r = case r of - (Action _) -> Dir rm (mappend fb r) - (Capture _ _ _) -> Dir rm (mappend fb r) - (Dir rm' fb') -> Dir (Map.unionWith mappend rm rm') (mappend fb fb') - NoRoute -> l - - ------------------------------------------------------------------------------- --- | A web handler which, given a mapping from URL entry points to web --- handlers, efficiently routes requests to the correct handler. --- --- The URL entry points are given as relative paths, for example: --- --- > route [ ("foo/bar/quux", fooBarQuux) ] --- --- If the URI of the incoming request is --- --- > /foo/bar/quux --- --- or --- --- > /foo/bar/quux/...anything... --- --- then the request will be routed to \"@fooBarQuux@\", with 'rqContextPath' --- set to \"@\/foo\/bar\/quux\/@\" and 'rqPathInfo' set to --- \"@...anything...@\". --- --- @FIXME@\/@TODO@: we need a version with and without the context path setting --- behaviour; if the route is \"@article\/:id\/print@\", we probably want the --- contextPath to be \"@\/article@\" instead of \"@\/article\/whatever\/print@\". --- --- A path component within an URL entry point beginning with a colon (\"@:@\") --- is treated as a /variable capture/; the corresponding path component within --- the request URI will be entered into the 'rqParams' parameters mapping with --- the given name. For instance, if the routes were: --- --- > route [ ("foo/:bar/baz", fooBazHandler) ] --- --- Then a request for \"@\/foo\/saskatchewan\/baz@\" would be routed to --- @fooBazHandler@ with a mapping for: --- --- > "bar" => "saskatchewan" --- --- in its parameters table. --- --- Longer paths are matched first, and specific routes are matched before --- captures. That is, if given routes: --- --- > [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] --- --- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will go --- to @h3@, and \"@\/a@\" will go to @h1@. --- --- The following example matches \"@\/article@\" to an article index, --- \"@\/login@\" to a login, and \"@\/article\/...@\" to an article renderer. --- --- > route [ ("article", renderIndex) --- > , ("article/:id", renderArticle) --- > , ("login", method POST doLogin) ] --- -route :: [(ByteString, Snap a)] -> Snap a -route rts = route' (return ()) rts' [] - where - rts' = mconcat (map pRoute rts) - - ------------------------------------------------------------------------------- --- | The 'routeLocal' function is the same as 'route', except it doesn't change --- the request's context path. This is useful if you want to route to a --- particular handler but you want that handler to receive the 'rqPathInfo' as --- it is. -routeLocal :: [(ByteString, Snap a)] -> Snap a -routeLocal rts' = do - req <- getRequest - let ctx = rqContextPath req - let p = rqPathInfo req - let md = modifyRequest $ \r -> r {rqContextPath=ctx, rqPathInfo=p} - - route' md rts [] <|> (md >> pass) - - where - rts = mconcat (map pRoute rts') - - ------------------------------------------------------------------------------- -pRoute :: (ByteString, Snap a) -> Route a -pRoute (r, a) = foldr f (Action a) hier - where - hier = filter (not . B.null) $ B.splitWith (== (c2w '/')) r - f s rt = if B.head s == c2w ':' - then Capture (B.tail s) rt NoRoute - else Dir (Map.fromList [(s, rt)]) NoRoute - - ------------------------------------------------------------------------------- -route' :: Snap () -- ^ an action to be run before any user - -- handler - -> Route a -- ^ currently active routing table - -> [Route a] -- ^ list of fallback routing tables in case - -- the current table fails - -> Snap a -route' pre (Action action) _ = pre >> action - -route' pre (Capture param rt fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - if B.null cwd - then route' pre fb fbs - else do localRequest (updateContextPath (B.length cwd) . (f cwd)) $ - route' pre rt (fb:fbs) - where - f v req = req { rqParams = Map.insertWith (++) param [v] (rqParams req) } - -route' pre (Dir rtm fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - case Map.lookup cwd rtm of - Just rt -> do - localRequest (updateContextPath (B.length cwd)) $ - route' pre rt (fb:fbs) - Nothing -> route' pre fb fbs - -route' _ NoRoute [] = pass -route' pre NoRoute (fb:fbs) = route' pre fb fbs -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Internal-Types.html b/static/docs/0.1.2/snap-core/src/Snap-Internal-Types.html deleted file mode 100644 index 47a4af5..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Internal-Types.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Types where - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Exception (throwIO, ErrorCall(..)) -import Control.Monad.CatchIO -import Control.Monad.State.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.IORef -import qualified Data.Iteratee as Iter -import Data.Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Data.Text.Lazy as LT -import qualified Data.Text.Lazy.Encoding as LT - -import Data.Typeable - ------------------------------------------------------------------------------- -import Snap.Iteratee hiding (Enumerator) -import Snap.Internal.Http.Types - - ------------------------------------------------------------------------------- --- The Snap Monad ------------------------------------------------------------------------------- - -{-| - -'Snap' is the 'Monad' that user web handlers run in. 'Snap' gives you: - -1. stateful access to fetch or modify an HTTP 'Request' - -2. stateful access to fetch or modify an HTTP 'Response' - -3. failure \/ 'Alternative' \/ 'MonadPlus' semantics: a 'Snap' handler can - choose not to handle a given request, using 'empty' or its synonym 'pass', - and you can try alternative handlers with the '<|>' operator: - - > a :: Snap String - > a = pass - > - > b :: Snap String - > b = return "foo" - > - > c :: Snap String - > c = a <|> b -- try running a, if it fails then try b - -4. convenience functions ('writeBS', 'writeLBS', 'writeText', 'writeLazyText', - 'addToOutput') for writing output to the 'Response': - - > a :: (forall a . Enumerator a) -> Snap () - > a someEnumerator = do - > writeBS "I'm a strict bytestring" - > writeLBS "I'm a lazy bytestring" - > addToOutput someEnumerator - -5. early termination: if you call 'finishWith': - - > a :: Snap () - > a = do - > modifyResponse $ setResponseStatus 500 "Internal Server Error" - > writeBS "500 error" - > r <- getResponse - > finishWith r - - then any subsequent processing will be skipped and supplied 'Response' value - will be returned from 'runSnap' as-is. - -6. access to the 'IO' monad through a 'MonadIO' instance: - - > a :: Snap () - > a = liftIO fireTheMissiles --} - - ------------------------------------------------------------------------------- -newtype Snap a = Snap { - unSnap :: StateT SnapState (Iteratee IO) (Maybe (Either Response a)) -} - - ------------------------------------------------------------------------------- -data SnapState = SnapState - { _snapRequest :: Request - , _snapResponse :: Response } - - ------------------------------------------------------------------------------- -instance Monad Snap where - (Snap m) >>= f = - Snap $ do - eth <- m - maybe (return Nothing) - (either (return . Just . Left) - (unSnap . f)) - eth - - return = Snap . return . Just . Right - fail = const $ Snap $ return Nothing - - ------------------------------------------------------------------------------- -instance MonadIO Snap where - liftIO m = Snap $ liftM (Just . Right) $ liftIO m - - ------------------------------------------------------------------------------- -instance MonadCatchIO Snap where - catch (Snap m) handler = Snap $ do - x <- try m - case x of - (Left e) -> let (Snap z) = handler e in z - (Right y) -> return y - - block (Snap m) = Snap $ block m - unblock (Snap m) = Snap $ unblock m - - ------------------------------------------------------------------------------- -instance MonadPlus Snap where - mzero = Snap $ return Nothing - - a `mplus` b = - Snap $ do - mb <- unSnap a - if isJust mb then return mb else unSnap b - - ------------------------------------------------------------------------------- -instance Functor Snap where - fmap = liftM - - ------------------------------------------------------------------------------- -instance Applicative Snap where - pure = return - (<*>) = ap - - ------------------------------------------------------------------------------- -instance Alternative Snap where - empty = mzero - (<|>) = mplus - - ------------------------------------------------------------------------------- -liftIter :: Iteratee IO a -> Snap a -liftIter i = Snap (lift i >>= return . Just . Right) - - ------------------------------------------------------------------------------- --- | Sends the request body through an iteratee (data consumer) and --- returns the result. -runRequestBody :: Iteratee IO a -> Snap a -runRequestBody iter = do - req <- getRequest - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - - -- make sure the iteratee consumes all of the output - let iter' = iter >>= (\a -> Iter.skipToEof >> return a) - - -- run the iteratee - result <- liftIter $ Iter.joinIM $ enum iter' - - -- stuff a new dummy enumerator into the request, so you can only try to - -- read the request body from the socket once - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . Iter.joinI . Iter.take 0 ) - - return result - - ------------------------------------------------------------------------------- --- | Returns the request body as a bytestring. -getRequestBody :: Snap L.ByteString -getRequestBody = liftM fromWrap $ runRequestBody stream2stream -{-# INLINE getRequestBody #-} - - ------------------------------------------------------------------------------- --- | Detaches the request body's 'Enumerator' from the 'Request' and --- returns it. You would want to use this if you needed to send the --- HTTP request body (transformed or otherwise) through to the output --- in O(1) space. (Examples: transcoding, \"echo\", etc) --- --- Normally Snap is careful to ensure that the request body is fully --- consumed after your web handler runs; this function is marked --- \"unsafe\" because it breaks this guarantee and leaves the --- responsibility up to you. If you don't fully consume the --- 'Enumerator' you get here, the next HTTP request in the pipeline --- (if any) will misparse. Be careful with exception handlers. -unsafeDetachRequestBody :: Snap (Enumerator a) -unsafeDetachRequestBody = do - req <- getRequest - let ioref = rqBody req - senum <- liftIO $ readIORef ioref - let (SomeEnumerator enum) = senum - liftIO $ writeIORef ioref - (SomeEnumerator $ return . Iter.joinI . Iter.take 0) - return enum - - ------------------------------------------------------------------------------- --- | Short-circuits a 'Snap' monad action early, storing the given --- 'Response' value in its state. -finishWith :: Response -> Snap () -finishWith = Snap . return . Just . Left -{-# INLINE finishWith #-} - - ------------------------------------------------------------------------------- --- | Fails out of a 'Snap' monad action. This is used to indicate --- that you choose not to handle the given request within the given --- handler. -pass :: Snap a -pass = empty - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only if the request's HTTP method matches --- the given method. -method :: Method -> Snap a -> Snap a -method m action = do - req <- getRequest - unless (rqMethod req == m) pass - action -{-# INLINE method #-} - - ------------------------------------------------------------------------------- --- Appends n bytes of the path info to the context path with a --- trailing slash. -updateContextPath :: Int -> Request -> Request -updateContextPath n req | n > 0 = req { rqContextPath = ctx - , rqPathInfo = pinfo } - | otherwise = req - where - ctx' = S.take n (rqPathInfo req) - ctx = S.concat [rqContextPath req, ctx', "/"] - pinfo = S.drop (n+1) (rqPathInfo req) - - ------------------------------------------------------------------------------- --- Runs a 'Snap' monad action only if the 'rqPathInfo' matches the given --- predicate. -pathWith :: (ByteString -> ByteString -> Bool) - -> ByteString - -> Snap a - -> Snap a -pathWith c p action = do - req <- getRequest - unless (c p (rqPathInfo req)) pass - localRequest (updateContextPath $ S.length p) action - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when the 'rqPathInfo' of the request --- starts with the given path. For example, --- --- > dir "foo" handler --- --- Will fail if 'rqPathInfo' is not \"@\/foo@\" or \"@\/foo\/...@\", and will --- add @\"foo\/\"@ to the handler's local 'rqContextPath'. -dir :: ByteString -- ^ path component to match - -> Snap a -- ^ handler to run - -> Snap a -dir = pathWith f - where - f dr pinfo = dr == x - where - (x,_) = S.break (=='/') pinfo -{-# INLINE dir #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is exactly --- equal to the given string. If the path matches, locally sets 'rqContextPath' --- to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\", and runs the given --- handler. -path :: ByteString -- ^ path to match against - -> Snap a -- ^ handler to run - -> Snap a -path = pathWith (==) -{-# INLINE path #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when 'rqPathInfo' is empty. -ifTop :: Snap a -> Snap a -ifTop = path "" -{-# INLINE ifTop #-} - - ------------------------------------------------------------------------------- --- | Local Snap version of 'get'. -sget :: Snap SnapState -sget = Snap $ liftM (Just . Right) get -{-# INLINE sget #-} - - ------------------------------------------------------------------------------- --- | Local Snap monad version of 'modify'. -smodify :: (SnapState -> SnapState) -> Snap () -smodify f = Snap $ modify f >> return (Just $ Right ()) -{-# INLINE smodify #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Request' object out of the 'Snap' monad. -getRequest :: Snap Request -getRequest = liftM _snapRequest sget -{-# INLINE getRequest #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Response' object out of the 'Snap' monad. -getResponse :: Snap Response -getResponse = liftM _snapResponse sget -{-# INLINE getResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Response' object into the 'Snap' monad. -putResponse :: Response -> Snap () -putResponse r = smodify $ \ss -> ss { _snapResponse = r } -{-# INLINE putResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Request' object into the 'Snap' monad. -putRequest :: Request -> Snap () -putRequest r = smodify $ \ss -> ss { _snapRequest = r } -{-# INLINE putRequest #-} - - ------------------------------------------------------------------------------- --- | Modifies the 'Request' object stored in a 'Snap' monad. -modifyRequest :: (Request -> Request) -> Snap () -modifyRequest f = smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss } -{-# INLINE modifyRequest #-} - - ------------------------------------------------------------------------------- --- | Modifes the 'Response' object stored in a 'Snap' monad. -modifyResponse :: (Response -> Response) -> Snap () -modifyResponse f = smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss } -{-# INLINE modifyResponse #-} - - ------------------------------------------------------------------------------- --- | Adds the output from the given enumerator to the 'Response' --- stored in the 'Snap' monad state. -addToOutput :: (forall a . Enumerator a) -- ^ output to add - -> Snap () -addToOutput enum = modifyResponse $ modifyResponseBody (>. enum) - - ------------------------------------------------------------------------------- --- | Adds the given strict 'ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeBS :: ByteString -> Snap () -writeBS s = addToOutput $ enumBS s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeLBS :: L.ByteString -> Snap () -writeLBS s = addToOutput $ enumLBS s - - ------------------------------------------------------------------------------- --- | Adds the given strict 'T.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeText :: T.Text -> Snap () -writeText s = writeBS $ T.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'LT.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeLazyText :: LT.Text -> Snap () -writeLazyText s = writeLBS $ LT.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Sets the output to be the contents of the specified file. --- --- Calling 'sendFile' will overwrite any output queued to be sent in the --- 'Response'. If the response body is not modified after the call to --- 'sendFile', Snap will use the efficient @sendfile()@ system call on --- platforms that support it. --- --- If the response body is modified (using 'modifyResponseBody'), the file will --- be read using @mmap()@. -sendFile :: FilePath -> Snap () -sendFile f = modifyResponse $ \r -> r { rspBody = SendFile f } - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' action with a locally-modified 'Request' state --- object. The 'Request' object in the Snap monad state after the call --- to localRequest will be unchanged. -localRequest :: (Request -> Request) -> Snap a -> Snap a -localRequest f m = do - req <- getRequest - - runAct req <|> (putRequest req >> pass) - - where - runAct req = do - modifyRequest f - result <- m - putRequest req - return result -{-# INLINE localRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Request' from state and hands it to the given action. -withRequest :: (Request -> Snap a) -> Snap a -withRequest = (getRequest >>=) -{-# INLINE withRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Response' from state and hands it to the given action. -withResponse :: (Response -> Snap a) -> Snap a -withResponse = (getResponse >>=) -{-# INLINE withResponse #-} - - ------------------------------------------------------------------------------- --- | This exception is thrown if the handler you supply to 'runSnap' fails. -data NoHandlerException = NoHandlerException - deriving (Eq, Typeable) - - ------------------------------------------------------------------------------- -instance Show NoHandlerException where - show NoHandlerException = "No handler for request" - - ------------------------------------------------------------------------------- -instance Exception NoHandlerException - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action in the 'Iteratee IO' monad. -runSnap :: Snap a -> Request -> Iteratee IO (Request,Response) -runSnap (Snap m) req = do - (r, ss') <- runStateT m ss - - e <- maybe (return $ Left fourohfour) - return - r - - -- is this a case of early termination? - let resp = case e of - Left x -> x - Right _ -> _snapResponse ss' - - return (_snapRequest ss', resp) - - where - fourohfour = setContentLength 3 $ - setResponseStatus 404 "Not Found" $ - modifyResponseBody (>. enumBS "404") $ - emptyResponse - - dresp = emptyResponse { rspHttpVersion = rqVersion req } - - ss = SnapState req dresp -{-# INLINE runSnap #-} - - ------------------------------------------------------------------------------- -evalSnap :: Snap a -> Request -> Iteratee IO a -evalSnap (Snap m) req = do - (r, _) <- runStateT m ss - - e <- maybe (liftIO $ throwIO NoHandlerException) - return - r - - -- is this a case of early termination? - case e of - Left _ -> liftIO $ throwIO $ ErrorCall "no value" - Right x -> return x - where - dresp = emptyResponse { rspHttpVersion = rqVersion req } - ss = SnapState req dresp -{-# INLINE evalSnap #-} - - - ------------------------------------------------------------------------------- --- | See 'rqParam'. Looks up a value for the given named parameter in the --- 'Request'. If more than one value was entered for the given parameter name, --- 'getParam' gloms the values together with: --- --- @ 'S.intercalate' \" \"@ --- -getParam :: ByteString -- ^ parameter name to look up - -> Snap (Maybe ByteString) -getParam k = do - rq <- getRequest - return $ liftM (S.intercalate " ") $ rqParam k rq - - -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Iteratee.html b/static/docs/0.1.2/snap-core/src/Snap-Iteratee.html deleted file mode 100644 index 0aecc98..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Iteratee.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} - --- | Snap Framework type aliases and utilities for iteratees. Note that as a --- convenience, this module also exports everything from @Data.Iteratee@ in the --- @iteratee@ library. --- --- /WARNING/: Note that all of these types are scheduled to change in the --- @darcs@ head version of the @iteratee@ library; John Lato et al. are working --- on a much improved iteratee formulation. - -module Snap.Iteratee - ( -- * Convenience aliases around types from @Data.Iteratee@ - Stream - , IterV - , Iteratee - , Enumerator - - -- * Re-export types and functions from @Data.Iteratee@ - , module Data.Iteratee - - -- * Helper functions - - -- ** Enumerators - , enumBS - , enumLBS - , enumFile - - -- ** Conversion to/from 'WrappedByteString' - , fromWrap - , toWrap - - -- ** Iteratee utilities - , takeExactly - , takeNoMoreThan - , countBytes - , bufferIteratee - ) where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad -import Control.Monad.CatchIO -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Lazy as L -import Data.Iteratee -import qualified Data.Iteratee.Base.StreamChunk as SC -import Data.Iteratee.WrappedByteString -import Data.Monoid (mappend) -import Data.Word (Word8) -import Prelude hiding (catch,drop) -import System.IO.Posix.MMap -import qualified Data.DList as D ------------------------------------------------------------------------------- - -type Stream = StreamG WrappedByteString Word8 -type IterV m = IterGV WrappedByteString Word8 m -type Iteratee m = IterateeG WrappedByteString Word8 m -type Enumerator m a = Iteratee m a -> m (Iteratee m a) - - ------------------------------------------------------------------------------- -instance (Functor m, MonadCatchIO m) => - MonadCatchIO (IterateeG s el m) where - --catch :: Exception e => m a -> (e -> m a) -> m a - catch m handler = IterateeG $ \str -> do - ee <- try $ runIter m str - case ee of - (Left e) -> runIter (handler e) str - (Right v) -> return v - - --block :: m a -> m a - block m = IterateeG $ \str -> block $ runIter m str - unblock m = IterateeG $ \str -> unblock $ runIter m str - - ------------------------------------------------------------------------------- --- | Wraps an 'Iteratee', counting the number of bytes consumed by it. -countBytes :: (Monad m) => Iteratee m a -> Iteratee m (a, Int) -countBytes = go 0 - where - go !n iter = IterateeG $ f n iter - - f !n !iter ch@(Chunk ws) = do - iterv <- runIter iter ch - case iterv of - Done x rest -> let !n' = n + m - len rest - in return $! Done (x, n') rest - Cont i err -> return $ Cont ((go $! n + m) i) err - where - m = S.length $ unWrap ws - - len (EOF _) = 0 - len (Chunk s) = S.length $ unWrap s - - f !n !iter stream = do - iterv <- runIter iter stream - case iterv of - Done x rest -> return $ Done (x, n) rest - Cont i err -> return $ Cont (go n i) err - - ------------------------------------------------------------------------------- --- | Buffers an iteratee. --- --- Our enumerators produce a lot of little strings; rather than spending all --- our time doing kernel context switches for 4-byte write() calls, we buffer --- the iteratee to send 2KB at a time. -bufferIteratee :: (Monad m) => Enumerator m a -bufferIteratee = return . go (D.empty,0) - where - blocksize = 2048 - - --go :: (DList ByteString, Int) -> Iteratee m a -> Iteratee m a - go (!dl,!n) iter = IterateeG $! f (dl,n) iter - - --f :: (DList ByteString, Int) -> Iteratee m a -> Stream -> m (IterV m a) - f _ !iter ch@(EOF (Just _)) = runIter iter ch - f (!dl,_) !iter ch@(EOF Nothing) = do - iterv <- runIter iter $ Chunk big - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> runIter i ch - where - big = toWrap $ L.fromChunks [S.concat $ D.toList dl] - - f (!dl,!n) iter (Chunk ws) = - if n' > blocksize - then do - iterv <- runIter iter (Chunk big) - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> return $ Cont (go (D.empty,0) i) Nothing - else return $ Cont (go (dl',n') iter) Nothing - where - s = S.concat $ L.toChunks $ fromWrap ws - m = S.length s - n' = n+m - dl' = D.snoc dl s - big = toWrap $ L.fromChunks [S.concat $ D.toList dl'] - - ------------------------------------------------------------------------------- --- | Enumerates a strict bytestring. -enumBS :: (Monad m) => ByteString -> Enumerator m a -enumBS bs = enumPure1Chunk $ WrapBS bs -{-# INLINE enumBS #-} - - ------------------------------------------------------------------------------- --- | Enumerates a lazy bytestring. -enumLBS :: (Monad m) => L.ByteString -> Enumerator m a -enumLBS lbs iter = foldM k iter enums - where - enums = map (enumPure1Chunk . WrapBS) $ L.toChunks lbs - k i e = e i - - ------------------------------------------------------------------------------- --- | Converts a lazy bytestring to a wrapped bytestring. -toWrap :: L.ByteString -> WrappedByteString Word8 -toWrap = WrapBS . S.concat . L.toChunks -{-# INLINE toWrap #-} - - ------------------------------------------------------------------------------- --- | Converts a wrapped bytestring to a lazy bytestring. -fromWrap :: WrappedByteString Word8 -> L.ByteString -fromWrap = L.fromChunks . (:[]) . unWrap -{-# INLINE fromWrap #-} - - ------------------------------------------------------------------------------- --- | Reads n elements from a stream and applies the given iteratee to --- the stream of the read elements. Reads exactly n elements, and if --- the stream is short propagates an error. -takeExactly :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeExactly 0 iter = return iter -takeExactly n' iter = - if n' < 0 - then takeExactly 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeExactly n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - step n (Chunk str) = done (Chunk s1) (Chunk s2) - where (s1, s2) = SC.splitAt n str - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n (EOF Nothing) = return $ Cont undefined (Just (Err "short write")) - check n (Done x _) = drop n >> return (return x) - check n (Cont x Nothing) = takeExactly n x - check n (Cont _ (Just e)) = drop n >> throwErr e - done s1 s2 = liftM (flip Done s2) (runIter iter s1 >>= checkIfDone return) - - ------------------------------------------------------------------------------- --- | Reads up to n elements from a stream and applies the given iteratee to the --- stream of the read elements. If more than n elements are read, propagates an --- error. -takeNoMoreThan :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeNoMoreThan n' iter = - if n' < 0 - then takeNoMoreThan 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeNoMoreThan n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - | otherwise = done (Chunk s1) (Chunk s2) - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - (s1, s2) = SC.splitAt n str - - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n chk@(EOF Nothing) = do - v <- runIter iter chk - - case v of - (Done x s) -> return $ Done (return x) s - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont _ Nothing) -> return $ Cont (throwErr $ Err "premature EOF") Nothing - - check _ v@(Done _ _) = return $ liftI v - check n (Cont x Nothing) = takeNoMoreThan n x - check _ (Cont _ (Just e)) = throwErr e - - done _ (EOF _) = error "impossible" - done s1 s2@(Chunk s2') = do - v <- runIter iter s1 - case v of - (Done x s') -> return $ Done (return x) (s' `mappend` s2) - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont i Nothing) -> - if SC.null s2' - then return $ Cont (takeNoMoreThan 0 i) Nothing - else return $ Cont undefined (Just $ Err "too many bytes") - - ------------------------------------------------------------------------------- -enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a) -enumFile fp iter = do - es <- (try $ - liftM WrapBS $ - unsafeMMapFile fp) :: IO (Either SomeException (WrappedByteString Word8)) - - case es of - (Left e) -> return $ throwErr $ Err $ "IO error" ++ show e - (Right s) -> liftM liftI $ runIter iter $ Chunk s -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Types.html b/static/docs/0.1.2/snap-core/src/Snap-Types.html deleted file mode 100644 index 69d5c03..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Types.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - -
{-| - -This module contains the core type definitions, class instances, and functions -for HTTP as well as the 'Snap' monad, which is used for web handlers. - --} -module Snap.Types - ( - -- * The Snap Monad - Snap - , runSnap - , NoHandlerException(..) - - -- ** Functions for control flow and early termination - , finishWith - , pass - - -- ** Routing - , method - , path - , dir - , ifTop - , route - , routeLocal - - -- ** Access to state - , getRequest - , getResponse - , putRequest - , putResponse - , modifyRequest - , modifyResponse - , localRequest - , withRequest - , withResponse - - -- ** Grabbing request bodies - , runRequestBody - , getRequestBody - , unsafeDetachRequestBody - -- * HTTP Datatypes and Functions - -- $httpDoc - -- - , Request - , Response - , Headers - , HasHeaders(..) - , Params - , Method(..) - , Cookie(..) - , HttpVersion - - -- ** Headers - , addHeader - , setHeader - , getHeader - - -- ** Requests - , rqServerName - , rqServerPort - , rqRemoteAddr - , rqRemotePort - , rqLocalAddr - , rqLocalHostname - , rqIsSecure - , rqContentLength - , rqMethod - , rqVersion - , rqCookies - , rqPathInfo - , rqContextPath - , rqURI - , rqQueryString - , rqParams - , rqParam - , getParam - , rqModifyParams - , rqSetParam - - -- ** Responses - , emptyResponse - , setResponseStatus - , rspStatus - , rspStatusReason - , setContentType - , addCookie - , setContentLength - , clearContentLength - - -- *** Response I/O - , setResponseBody - , modifyResponseBody - , addToOutput - , writeBS - , writeLazyText - , writeText - , writeLBS - , sendFile - - -- * Iteratee - , Enumerator - - -- * HTTP utilities - , formatHttpTime - , parseHttpTime - , urlEncode - , urlDecode - ) where - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Routing -import Snap.Internal.Types ------------------------------------------------------------------------------- - --- $httpDoc --- HTTP-related datatypes: 'Request', 'Response', 'Cookie', etc. -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Util-FileServe.html b/static/docs/0.1.2/snap-core/src/Snap-Util-FileServe.html deleted file mode 100644 index 38c9a3f..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Util-FileServe.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | Contains web handlers to serve files from a directory. -module Snap.Util.FileServe -( - getSafePath -, fileServe -, fileServe' -, fileServeSingle -, fileServeSingle' -, defaultMimeTypes -, MimeMap -) where - ------------------------------------------------------------------------------- -import Control.Monad -import Control.Monad.Trans -import qualified Data.ByteString.Char8 as S -import Data.ByteString.Char8 (ByteString) -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import System.Directory -import System.FilePath -import System.Posix.Files - ------------------------------------------------------------------------------- -import Snap.Types - - ------------------------------------------------------------------------------- --- | A type alias for MIME type -type MimeMap = Map FilePath ByteString - - ------------------------------------------------------------------------------- --- | The default set of mime type mappings we use when serving files. Its --- value: --- --- > Map.fromList [ --- > ( ".asc" , "text/plain" ), --- > ( ".asf" , "video/x-ms-asf" ), --- > ( ".asx" , "video/x-ms-asf" ), --- > ( ".avi" , "video/x-msvideo" ), --- > ( ".bz2" , "application/x-bzip" ), --- > ( ".c" , "text/plain" ), --- > ( ".class" , "application/octet-stream" ), --- > ( ".conf" , "text/plain" ), --- > ( ".cpp" , "text/plain" ), --- > ( ".css" , "text/css" ), --- > ( ".cxx" , "text/plain" ), --- > ( ".dtd" , "text/xml" ), --- > ( ".dvi" , "application/x-dvi" ), --- > ( ".gif" , "image/gif" ), --- > ( ".gz" , "application/x-gzip" ), --- > ( ".hs" , "text/plain" ), --- > ( ".htm" , "text/html" ), --- > ( ".html" , "text/html" ), --- > ( ".jar" , "application/x-java-archive" ), --- > ( ".jpeg" , "image/jpeg" ), --- > ( ".jpg" , "image/jpeg" ), --- > ( ".js" , "text/javascript" ), --- > ( ".log" , "text/plain" ), --- > ( ".m3u" , "audio/x-mpegurl" ), --- > ( ".mov" , "video/quicktime" ), --- > ( ".mp3" , "audio/mpeg" ), --- > ( ".mpeg" , "video/mpeg" ), --- > ( ".mpg" , "video/mpeg" ), --- > ( ".ogg" , "application/ogg" ), --- > ( ".pac" , "application/x-ns-proxy-autoconfig" ), --- > ( ".pdf" , "application/pdf" ), --- > ( ".png" , "image/png" ), --- > ( ".ps" , "application/postscript" ), --- > ( ".qt" , "video/quicktime" ), --- > ( ".sig" , "application/pgp-signature" ), --- > ( ".spl" , "application/futuresplash" ), --- > ( ".swf" , "application/x-shockwave-flash" ), --- > ( ".tar" , "application/x-tar" ), --- > ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), --- > ( ".tar.gz" , "application/x-tgz" ), --- > ( ".tbz" , "application/x-bzip-compressed-tar" ), --- > ( ".text" , "text/plain" ), --- > ( ".tgz" , "application/x-tgz" ), --- > ( ".torrent" , "application/x-bittorrent" ), --- > ( ".txt" , "text/plain" ), --- > ( ".wav" , "audio/x-wav" ), --- > ( ".wax" , "audio/x-ms-wax" ), --- > ( ".wma" , "audio/x-ms-wma" ), --- > ( ".wmv" , "video/x-ms-wmv" ), --- > ( ".xbm" , "image/x-xbitmap" ), --- > ( ".xml" , "text/xml" ), --- > ( ".xpm" , "image/x-xpixmap" ), --- > ( ".xwd" , "image/x-xwindowdump" ), --- > ( ".zip" , "application/zip" ) ] --- -defaultMimeTypes :: MimeMap -defaultMimeTypes = Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".ttf" , "application/x-font-truetype" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - ------------------------------------------------------------------------------- --- | Gets a path from the 'Request' using 'rqPathInfo' and makes sure it is --- safe to use for opening files. A path is safe if it is a relative path --- and has no ".." elements to escape the intended directory structure. -getSafePath :: Snap FilePath -getSafePath = do - req <- getRequest - let p = S.unpack $ rqPathInfo req - - -- check that we don't have any sneaky .. paths - let dirs = splitDirectories p - when (elem ".." dirs) pass - return p - - ------------------------------------------------------------------------------- --- | Serves files out of the given directory. The relative path given in --- 'rqPathInfo' is searched for the given file, and the file is served with the --- appropriate mime type if it is found. Absolute paths and \"@..@\" are prohibited --- to prevent files from being served from outside the sandbox. --- --- Uses 'defaultMimeTypes' to determine the @Content-Type@ based on the file's --- extension. -fileServe :: FilePath -- ^ root directory - -> Snap () -fileServe = fileServe' defaultMimeTypes -{-# INLINE fileServe #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServe', with control over the MIME mapping used. -fileServe' :: MimeMap -- ^ MIME type mapping - -> FilePath -- ^ root directory - -> Snap () -fileServe' mm root = do - sp <- getSafePath - let fp = root </> sp - - -- check that the file exists - liftIO (doesFileExist fp) >>= flip unless pass - - let fn = takeFileName fp - let mime = fileType mm fn - fileServeSingle' mime fp -{-# INLINE fileServe' #-} - - ------------------------------------------------------------------------------- --- | Serves a single file specified by a full or relative path. The --- path restrictions on fileServe don't apply to this function since --- the path is not being supplied by the user. -fileServeSingle :: FilePath -- ^ path to file - -> Snap () -fileServeSingle fp = - fileServeSingle' (fileType defaultMimeTypes (takeFileName fp)) fp -{-# INLINE fileServeSingle #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServeSingle', with control over the MIME mapping used. -fileServeSingle' :: ByteString -- ^ MIME type mapping - -> FilePath -- ^ path to file - -> Snap () -fileServeSingle' mime fp = do - req <- getRequest - - let mbH = getHeader "if-modified-since" req - mbIfModified <- liftIO $ case mbH of - Nothing -> return Nothing - (Just s) -> liftM Just $ parseHttpTime s - - -- check modification time and bug out early if the file is not modified. - filestat <- liftIO $ getFileStatus fp - let mt = modificationTime filestat - maybe (return ()) (chkModificationTime mt) mbIfModified - - let sz = fromEnum $ fileSize filestat - lm <- liftIO $ formatHttpTime mt - - modifyResponse $ setHeader "Last-Modified" lm - . setContentType mime - . setContentLength sz - sendFile fp - - where - -------------------------------------------------------------------------- - chkModificationTime mt lt = when (mt <= lt) notModified - - -------------------------------------------------------------------------- - notModified = finishWith $ - setResponseStatus 304 "Not Modified" emptyResponse - - ------------------------------------------------------------------------------- -fileType :: MimeMap -> FilePath -> ByteString -fileType mm f = - if null ext - then defaultMimeType - else fromMaybe (fileType mm (drop 1 ext)) - mbe - - where - ext = takeExtensions f - mbe = Map.lookup ext mm - - ------------------------------------------------------------------------------- -defaultMimeType :: ByteString -defaultMimeType = "application/octet-stream" -- diff --git a/static/docs/0.1.2/snap-core/src/Snap-Util-GZip.html b/static/docs/0.1.2/snap-core/src/Snap-Util-GZip.html deleted file mode 100644 index 6119f1d..0000000 --- a/static/docs/0.1.2/snap-core/src/Snap-Util-GZip.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Util.GZip -( withCompression -, withCompression' ) where - -import qualified Codec.Compression.GZip as GZip -import qualified Codec.Compression.Zlib as Zlib -import Control.Concurrent -import Control.Applicative hiding (many) -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.Attoparsec.Char8 hiding (Done) -import qualified Data.Attoparsec.Char8 as Atto -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Char8 (ByteString) -import Data.Iteratee.WrappedByteString -import Data.Maybe -import qualified Data.Set as Set -import Data.Set (Set) -import Data.Typeable -import Prelude hiding (catch, takeWhile) - ------------------------------------------------------------------------------- -import Snap.Internal.Debug -import Snap.Iteratee hiding (Enumerator) -import Snap.Types - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' web handler with compression if available. --- --- If the client has indicated support for @gzip@ or @compress@ in its --- @Accept-Encoding@ header, and the @Content-Type@ in the response is one of --- the following types: --- --- * @application/x-javascript@ --- --- * @text/css@ --- --- * @text/html@ --- --- * @text/javascript@ --- --- * @text/plain@ --- --- * @text/xml@ --- --- * @application/x-font-truetype@ --- --- Then the given handler's output stream will be compressed, --- @Content-Encoding@ will be set in the output headers, and the --- @Content-Length@ will be cleared if it was set. (We can't process the stream --- in O(1) space if the length is known beforehand.) --- --- The wrapped handler will be run to completion, and then the 'Response' --- that's contained within the 'Snap' monad state will be passed to --- 'finishWith' to prevent further processing. --- -withCompression :: Snap a -- ^ the web handler to run - -> Snap () -withCompression = withCompression' compressibleMimeTypes - - ------------------------------------------------------------------------------- --- | The same as 'withCompression', with control over which MIME types to --- compress. -withCompression' :: Set ByteString - -- ^ set of compressible MIME types - -> Snap a - -- ^ the web handler to run - -> Snap () -withCompression' mimeTable action = do - _ <- action - resp <- getResponse - - -- If a content-encoding is already set, do nothing. This prevents - -- "withCompression $ withCompression m" from ruining your day. - if isJust $ getHeader "Content-Encoding" resp - then return () - else do - let mbCt = getHeader "Content-Type" resp - - debug $ "withCompression', content-type is " ++ show mbCt - - case mbCt of - (Just ct) -> if Set.member ct mimeTable - then chkAcceptEncoding - else return () - _ -> return () - - - getResponse >>= finishWith - - where - chkAcceptEncoding :: Snap () - chkAcceptEncoding = do - req <- getRequest - debug $ "checking accept-encoding" - let mbAcc = getHeader "Accept-Encoding" req - debug $ "accept-encoding is " ++ show mbAcc - let s = fromMaybe "" mbAcc - - types <- liftIO $ parseAcceptEncoding s - - chooseType types - - - chooseType [] = return () - chooseType ("gzip":_) = gzipCompression - chooseType ("compress":_) = compressCompression - chooseType ("x-gzip":_) = gzipCompression - chooseType ("x-compress":_) = compressCompression - chooseType (_:xs) = chooseType xs - - ------------------------------------------------------------------------------- --- private following ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -compressibleMimeTypes :: Set ByteString -compressibleMimeTypes = Set.fromList [ "application/x-font-truetype" - , "application/x-javascript" - , "text/css" - , "text/html" - , "text/javascript" - , "text/plain" - , "text/xml" ] - - - - ------------------------------------------------------------------------------- -gzipCompression :: Snap () -gzipCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "gzip" . - clearContentLength . - modifyResponseBody gcompress - - ------------------------------------------------------------------------------- -compressCompression :: Snap () -compressCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "compress" . - clearContentLength . - modifyResponseBody ccompress - - ------------------------------------------------------------------------------- -gcompress :: forall a . Enumerator a -> Enumerator a -gcompress = compressEnumerator GZip.compress - - ------------------------------------------------------------------------------- -ccompress :: forall a . Enumerator a -> Enumerator a -ccompress = compressEnumerator Zlib.compress - - ------------------------------------------------------------------------------- -compressEnumerator :: forall a . - (L.ByteString -> L.ByteString) - -> Enumerator a - -> Enumerator a -compressEnumerator compFunc enum iteratee = do - writeEnd <- newChan - readEnd <- newChan - tid <- forkIO $ threadProc readEnd writeEnd - - enum (IterateeG $ f readEnd writeEnd tid iteratee) - - where - -------------------------------------------------------------------------- - streamFinished :: Stream -> Bool - streamFinished (EOF _) = True - streamFinished (Chunk _) = False - - - -------------------------------------------------------------------------- - consumeSomeOutput :: Chan Stream - -> Iteratee IO a - -> IO (Iteratee IO a) - consumeSomeOutput writeEnd iter = do - e <- isEmptyChan writeEnd - if e - then return iter - else do - ch <- readChan writeEnd - - iter' <- liftM liftI $ runIter iter ch - if (streamFinished ch) - then return iter' - else consumeSomeOutput writeEnd iter' - - - -------------------------------------------------------------------------- - consumeRest :: Chan Stream - -> Iteratee IO a - -> IO (IterV IO a) - consumeRest writeEnd iter = do - ch <- readChan writeEnd - - iv <- runIter iter ch - if (streamFinished ch) - then return iv - else consumeRest writeEnd $ liftI iv - - - -------------------------------------------------------------------------- - f readEnd writeEnd tid i (EOF Nothing) = do - writeChan readEnd Nothing - x <- consumeRest writeEnd i - killThread tid - return x - - f _ _ tid i ch@(EOF (Just _)) = do - x <- runIter i ch - killThread tid - return x - - f readEnd writeEnd tid i (Chunk s') = do - let s = unWrap s' - writeChan readEnd $ Just s - i' <- consumeSomeOutput writeEnd i - return $ Cont (IterateeG $ f readEnd writeEnd tid i') Nothing - - - -------------------------------------------------------------------------- - threadProc :: Chan (Maybe ByteString) - -> Chan Stream - -> IO () - threadProc readEnd writeEnd = do - stream <- getChanContents readEnd - let bs = L.fromChunks $ streamToChunks stream - - let output = L.toChunks $ compFunc bs - let runIt = do - mapM_ (writeChan writeEnd . toChunk) output - writeChan writeEnd $ EOF Nothing - - runIt `catch` \(e::SomeException) -> - writeChan writeEnd $ EOF (Just $ Err $ show e) - - - -------------------------------------------------------------------------- - streamToChunks [] = [] - streamToChunks (Nothing:_) = [] - streamToChunks ((Just x):xs) = x:(streamToChunks xs) - - - -------------------------------------------------------------------------- - toChunk = Chunk . WrapBS - - ------------------------------------------------------------------------------- -fullyParse :: ByteString -> Parser a -> Either String a -fullyParse s p = - case r' of - (Fail _ _ e) -> Left e - (Partial _) -> Left "parse failed" - (Atto.Done _ x) -> Right x - where - r = parse p s - r' = feed r "" - - ------------------------------------------------------------------------------- --- We're not gonna bother with quality values; we'll do gzip or compress in --- that order. -acceptParser :: Parser [ByteString] -acceptParser = do - xs <- option [] $ (:[]) <$> encoding - ys <- many (char ',' *> encoding) - endOfInput - return $ xs ++ ys - where - encoding = skipSpace *> c <* skipSpace - - c = do - x <- coding - option () qvalue - return x - - qvalue = do - skipSpace - char ';' - skipSpace - char 'q' - skipSpace - char '=' - float - return () - - coding = string "*" <|> takeWhile isCodingChar - - isCodingChar c = isAlpha_ascii c || c == '-' - - float = takeWhile isDigit >> - option () (char '.' >> takeWhile isDigit >> pure ()) - - ------------------------------------------------------------------------------- -data BadAcceptEncodingException = BadAcceptEncodingException - deriving (Typeable) - - ------------------------------------------------------------------------------- -instance Show BadAcceptEncodingException where - show BadAcceptEncodingException = "bad 'accept-encoding' header" - - ------------------------------------------------------------------------------- -instance Exception BadAcceptEncodingException - - ------------------------------------------------------------------------------- -parseAcceptEncoding :: ByteString -> IO [ByteString] -parseAcceptEncoding s = - case r of - Left _ -> throwIO BadAcceptEncodingException - Right x -> return x - where - r = fullyParse s acceptParser - -- diff --git a/static/docs/0.1.2/snap-core/src/hscolour.css b/static/docs/0.1.2/snap-core/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.2/snap-core/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.1.2/snap-server/Snap-Http-Server-Config.html b/static/docs/0.1.2/snap-server/Snap-Http-Server-Config.html deleted file mode 100644 index 52bc6c1..0000000 --- a/static/docs/0.1.2/snap-server/Snap-Http-Server-Config.html +++ /dev/null @@ -1,287 +0,0 @@ - - -
| ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||
| ||||||||||||||||
Description | ||||||||||||||||
The Snap HTTP server is a high performance, epoll-enabled, iteratee-based - web server library written in Haskell. Together with the snap-core library - upon which it depends, it provides a clean and efficient Haskell programming - interface to the HTTP protocol. - | ||||||||||||||||
Synopsis | ||||||||||||||||
| ||||||||||||||||
Documentation | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||
| ||||||||||||||||||
Synopsis | ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Prepares a log message with the time prepended. - | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Creates a new logger, logging to the given file. If the file argument is - "-", then log to stdout; if it's "stderr" then we log to stderr, - otherwise we log to a regular file in append mode. The file is closed and - re-opened every 15 minutes to facilitate external log rotation. - | ||||||||||||||||||
| ||||||||||||||||||
Sends out a log message verbatim with a newline appended. Note: - if you want a fancy log message you'll have to format it yourself - (or use combinedLogEntry). - | ||||||||||||||||||
| ||||||||||||||||||
Kills a logger thread, causing any unwritten contents to be - flushed out to disk - | ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||
snap-server-0.1.1: A fast, iteratee-based, epoll-enabled web server for the Snap Framework | ||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - The Snap HTTP server is a high performance, epoll-enabled, iteratee-based web -server library written in Haskell. Together with the snap-core library upon -which it depends, it provides a clean and efficient Haskell programming -interface to the HTTP protocol. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Snap.Http.Server.Config - ( Config(..) - , readConfigFromCmdLineArgs - ) where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import Data.ByteString.Internal (c2w) -import Data.ByteString.Char8 () -import Data.Maybe -import Data.Monoid -import System.Console.GetOpt -import System.Environment -import System.Exit -import System.IO - -data Config = Config - { localHostname :: !ByteString - , bindAddress :: !ByteString - , listenPort :: !Int - , accessLog :: !(Maybe FilePath) - , errorLog :: !(Maybe FilePath) - } deriving (Show) - - -data Flag = Flag - { flagLocalHost :: Maybe String - , flagBindAddress :: Maybe String - , flagPort :: Maybe Int - , flagAccessLog :: Maybe String - , flagErrorLog :: Maybe String - , flagUsage :: Bool - } - -instance Monoid Flag where - mempty = Flag Nothing Nothing Nothing Nothing Nothing False - - (Flag a1 b1 c1 d1 e1 f1) `mappend` (Flag a2 b2 c2 d2 e2 f2) = - Flag (getLast $ Last a1 `mappend` Last a2) - (getLast $ Last b1 `mappend` Last b2) - (getLast $ Last c1 `mappend` Last c2) - (getLast $ Last d1 `mappend` Last d2) - (getLast $ Last e1 `mappend` Last e2) - (f1 || f2) - -flagLH :: String -> Flag -flagLH s = mempty { flagLocalHost = Just s } - -flagBA :: String -> Flag -flagBA s = mempty { flagBindAddress = Just s } - -flagPt :: String -> Flag -flagPt p = mempty { flagPort = Just (read p) } - -flagAL :: String -> Flag -flagAL s = mempty { flagAccessLog = Just s } - -flagEL :: String -> Flag -flagEL s = mempty { flagErrorLog = Just s } - -flagHelp :: Flag -flagHelp = mempty { flagUsage = True } - -fromStr :: String -> ByteString -fromStr = B.pack . map c2w - -flags2config :: Flag -> Config -flags2config (Flag a b c d e _) = - Config (maybe "localhost" fromStr a) - (maybe "*" fromStr b) - (fromMaybe 8888 c) - d - e - - -options :: [OptDescr Flag] -options = - [ Option "l" ["localHostname"] - (ReqArg flagLH "STR") - "local hostname, default 'localhost'" - , Option "p" ["listenPort"] - (ReqArg flagPt "NUM") - "port to listen on, default 8888" - , Option "b" ["bindAddress"] - (ReqArg flagBA "STR") - "address to bind to, default '*'" - , Option "a" ["accessLog"] - (ReqArg flagAL "STR") - "access log in the 'combined' format, optional" - , Option "e" ["errorLog"] - (ReqArg flagEL "STR") - "error log, optional" - , Option "h" ["help"] - (NoArg flagHelp) - "display this usage statement" ] - - -readConfigFromCmdLineArgs :: String -- ^ application description, e.g. - -- \"Foo applet v0.2\" - -> IO Config -readConfigFromCmdLineArgs appName = do - argv <- getArgs - progName <- getProgName - - case getOpt Permute options argv of - (f,_,[] ) -> withFlags progName f - (_,_,errs) -> bombout progName errs - where - bombout progName errs = do - let hdr = appName ++ "\n\nUsage: " ++ progName ++ " [OPTIONS]" - let msg = concat errs ++ usageInfo hdr options - hPutStrLn stderr msg - exitFailure - - withFlags progName fs = do - let f = mconcat fs - if flagUsage f - then bombout progName [] - else return $ flags2config f -- diff --git a/static/docs/0.1.2/snap-server/src/Snap-Http-Server.html b/static/docs/0.1.2/snap-server/src/Snap-Http-Server.html deleted file mode 100644 index dbe6ebb..0000000 --- a/static/docs/0.1.2/snap-server/src/Snap-Http-Server.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -
-- | The Snap HTTP server is a high performance, epoll-enabled, iteratee-based --- web server library written in Haskell. Together with the @snap-core@ library --- upon which it depends, it provides a clean and efficient Haskell programming --- interface to the HTTP protocol. -module Snap.Http.Server -( - httpServe -) where - -import Data.ByteString (ByteString) -import Snap.Types -import qualified Snap.Internal.Http.Server as Int - - --- | Starts serving HTTP requests on the given port using the given handler. --- This function never returns; to shut down the HTTP server, kill the --- controlling thread. -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the (optional) access log - -> Maybe FilePath -- ^ path to the (optional) error log - -> Snap () -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alog elog handler = - Int.httpServe bindAddress bindPort localHostname alog elog handler' - where - handler' = runSnap handler -- diff --git a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Parser.html b/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Parser.html deleted file mode 100644 index a50a1de..0000000 --- a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Parser.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Http.Parser - ( IRequest(..) - , parseRequest - , readChunkedTransferEncoding - , parserToIteratee - , parseCookie - , parseUrlEncoded - , writeChunkedTransferEncoding - , strictize - ) where - - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Arrow (first, second) -import Control.Monad (liftM) -import Control.Monad.Trans -import Data.Attoparsec hiding (many, Result(..)) -import Data.Attoparsec.Iteratee -import Data.Bits -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Lazy as L -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import Data.Char -import Data.CIByteString -import Data.List (foldl') -import Data.Int -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (catMaybes, fromMaybe) -import Data.Time.Format (parseTime) -import qualified Data.Vector.Unboxed as Vec -import Data.Vector.Unboxed (Vector) -import Data.Word (Word8, Word64) -import Prelude hiding (take, takeWhile) -import System.Locale (defaultTimeLocale) ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Iteratee hiding (take, foldl') - - - ------------------------------------------------------------------------------- --- | an internal version of the headers part of an HTTP request -data IRequest = IRequest - { iMethod :: Method - , iRequestUri :: ByteString - , iHttpVersion :: (Int,Int) - , iRequestHeaders :: [(ByteString, ByteString)] - } - -instance Show IRequest where - show (IRequest m u v r) = - concat [ show m - , " " - , show u - , " " - , show v - , " " - , show r ] - ------------------------------------------------------------------------------- -parseRequest :: (Monad m) => Iteratee m (Maybe IRequest) -parseRequest = parserToIteratee pRequest - - -readChunkedTransferEncoding :: (Monad m) => Enumerator m a -readChunkedTransferEncoding iter = do - i <- chunkParserToEnumerator (parserToIteratee pGetTransferChunk) - iter - - return i - - -toHex :: Int64 -> ByteString -toHex !i' = S.reverse s - where - !i = abs i' - (!s,_) = S.unfoldrN 16 f (fromIntegral i) - - f :: Word64 -> Maybe (Word8, Word64) - f d = if d == 0 - then Nothing - else Just (ch, theRest) - - where - low4 = fromIntegral $ d .&. 0xf - ch = if low4 >= 10 - then c2w 'a' + low4 - 10 - else c2w '0' + low4 - theRest = (d .&. (complement 0xf)) `shiftR` 4 - - --- | Given an iteratee, produces a new one that wraps chunks sent to it with a --- chunked transfer-encoding. Example usage: --- --- > > (writeChunkedTransferEncoding --- > (enumLBS (L.fromChunks ["foo","bar","quux"])) --- > stream2stream) >>= --- > run >>= --- > return . fromWrap --- > --- > Chunk "3\r\nfoo\r\n3\r\nbar\r\n4\r\nquux\r\n0\r\n\r\n" Empty --- -writeChunkedTransferEncoding :: (Monad m) => Enumerator m a -> Enumerator m a -writeChunkedTransferEncoding enum it = do - i <- wrap it - enum i - - where - wrap iter = return $ IterateeG $ \s -> - case s of - (EOF Nothing) -> do - v <- runIter iter (Chunk $ toWrap "0\r\n\r\n") - i <- checkIfDone return v - runIter i (EOF Nothing) - (EOF e) -> return $ Cont undefined e - (Chunk x') -> do - let x = S.concat $ L.toChunks $ fromWrap x' - let n = S.length x - let o = L.fromChunks [ toHex (toEnum n) - , "\r\n" - , x - , "\r\n" ] - v <- runIter iter (Chunk $ toWrap o) - i <- checkIfDone wrap v - return $ Cont i Nothing - - -chunkParserToEnumerator :: (Monad m) => - Iteratee m (Maybe ByteString) - -> Iteratee m a - -> m (Iteratee m a) -chunkParserToEnumerator getChunk client = return $ do - mbB <- getChunk - maybe (finishIt client) (sendBS client) mbB - - where - sendBS iter s = do - v <- lift $ runIter iter (Chunk $ toWrap $ L.fromChunks [s]) - - case v of - (Done _ (EOF (Just e))) -> throwErr e - - (Done x _) -> return x - - (Cont _ (Just e)) -> throwErr e - - (Cont k Nothing) -> joinIM $ - chunkParserToEnumerator getChunk k - - finishIt iter = do - e <- lift $ sendEof iter - - case e of - Left x -> throwErr x - Right x -> return x - - sendEof iter = do - v <- runIter iter (EOF Nothing) - - return $ case v of - (Done _ (EOF (Just e))) -> Left e - (Done x _) -> Right x - (Cont _ (Just e)) -> Left e - (Cont _ _) -> Left $ Err $ "divergent iteratee" - - ------------------------------------------------------------------------------- --- parse functions ------------------------------------------------------------------------------- - --- theft alert: many of these routines adapted from Johan Tibell's hyena --- package - --- | Parsers for different tokens in an HTTP request. -sp, digit, letter :: Parser Word8 -sp = word8 $ c2w ' ' -digit = satisfy (isDigit . w2c) -letter = satisfy (isAlpha . w2c) - -untilEOL :: Parser ByteString -untilEOL = takeWhile notend - where - notend d = let c = w2c d in not $ c == '\r' || c == '\n' - -crlf :: Parser ByteString -crlf = string "\r\n" - --- | Parser for zero or more spaces. -spaces :: Parser [Word8] -spaces = many sp - -pSpaces :: Parser ByteString -pSpaces = takeWhile (isSpace . w2c) - --- | Parser for the internal request data type. -pRequest :: Parser (Maybe IRequest) -pRequest = (Just <$> pRequest') <|> (endOfInput *> pure Nothing) - -pRequest' :: Parser IRequest -pRequest' = IRequest - <$> (option "" crlf *> pMethod) <* sp - <*> pUri <* sp - <*> pVersion <* crlf - <*> pHeaders <* crlf - - -- note: the optional crlf is at the beginning because some older browsers - -- send an extra crlf after a POST body - - --- | Parser for the request method. -pMethod :: Parser Method -pMethod = (OPTIONS <$ string "OPTIONS") - <|> (GET <$ string "GET") - <|> (HEAD <$ string "HEAD") - <|> word8 (c2w 'P') *> ((POST <$ string "OST") <|> - (PUT <$ string "UT")) - <|> (DELETE <$ string "DELETE") - <|> (TRACE <$ string "TRACE") - <|> (CONNECT <$ string "CONNECT") - --- | Parser for the request URI. -pUri :: Parser ByteString -pUri = takeWhile (not . isSpace . w2c) - --- | Parser for the request's HTTP protocol version. -pVersion :: Parser (Int, Int) -pVersion = string "HTTP/" *> - liftA2 (,) (digit' <* word8 (c2w '.')) digit' - where - digit' = fmap (digitToInt . w2c) digit - -fieldChars :: Parser ByteString -fieldChars = takeWhile isFieldChar - where - isFieldChar c = (Vec.!) fieldCharTable (fromEnum c) - -fieldCharTable :: Vector Bool -fieldCharTable = Vec.generate 256 f - where - f d = let c=toEnum d in (isDigit c) || (isAlpha c) || c == '-' || c == '_' - - --- | Parser for request headers. -pHeaders :: Parser [(ByteString, ByteString)] -pHeaders = many header - where - header = {-# SCC "pHeaders/header" #-} - liftA2 (,) - fieldName - (word8 (c2w ':') *> spaces *> contents) - - fieldName = {-# SCC "pHeaders/fieldName" #-} - liftA2 S.cons letter fieldChars - - contents = {-# SCC "pHeaders/contents" #-} - liftA2 S.append - (untilEOL <* crlf) - (continuation <|> pure S.empty) - - isLeadingWS w = {-# SCC "pHeaders/isLeadingWS" #-} - elem w wstab - - wstab = map c2w " \t" - - leadingWhiteSpace = {-# SCC "pHeaders/leadingWhiteSpace" #-} - takeWhile1 isLeadingWS - - continuation = {-# SCC "pHeaders/continuation" #-} - liftA2 S.cons - (leadingWhiteSpace *> pure (c2w ' ')) - contents - - -pGetTransferChunk :: Parser (Maybe ByteString) -pGetTransferChunk = do - !hex <- liftM fromHex $ (takeWhile (isHexDigit . w2c)) - takeTill ((== '\r') . w2c) - crlf - if hex <= 0 - then return Nothing - else do - x <- take hex - crlf - return $ Just x - where - fromHex :: ByteString -> Int - fromHex s = Cvt.hex (L.fromChunks [s]) - - ------------------------------------------------------------------------------- --- COOKIE PARSING ------------------------------------------------------------------------------- - --- these definitions try to mirror RFC-2068 (the HTTP/1.1 spec) and RFC-2109 --- (cookie spec): please point out any errors! - -{-# INLINE matchAll #-} -matchAll :: [ Char -> Bool ] -> Char -> Bool -matchAll x c = and $ map ($ c) x - -{-# INLINE isToken #-} -isToken :: Char -> Bool -isToken c = (Vec.!) tokenTable (fromEnum c) - where - tokenTable :: Vector Bool - tokenTable = Vec.generate 256 (f . toEnum) - - f = matchAll [ isAscii - , not . isControl - , not . isSpace - , not . flip elem [ '(', ')', '<', '>', '@', ',', ';' - , ':', '\\', '\"', '/', '[', ']' - , '?', '=', '{', '}' ] - ] - -{-# INLINE isRFCText #-} -isRFCText :: Char -> Bool -isRFCText = not . isControl - -pToken :: Parser ByteString -pToken = takeWhile (isToken . w2c) - - -pQuotedString :: Parser ByteString -pQuotedString = q *> quotedText <* q - where - quotedText = (S.concat . reverse) <$> f [] - - f soFar = do - t <- takeWhile qdtext - - let soFar' = t:soFar - - -- RFC says that backslash only escapes for <"> - choice [ string "\\\"" *> f ("\"" : soFar') - , pure soFar' ] - - - q = word8 $ c2w '\"' - - qdtext = matchAll [ isRFCText, (/= '\"'), (/= '\\') ] . w2c - - -pCookie :: Parser Cookie -pCookie = do - -- grab kvps and turn to strict bytestrings - kvps <- pAvPairs - - -- kvps guaranteed non-null due to grammar. First avpair specifies - -- name=value mapping. - let ((nm,val):attrs') = kvps - let attrs = map (first toCI) attrs' - - -- and we'll gather the rest of the fields with helper functions. - return $ foldl' field (nullCookie nm val) attrs - - - where - nullCookie nm val = Cookie nm val Nothing Nothing Nothing - - fieldFuncs :: [ (CIByteString, Cookie -> ByteString -> Cookie) ] - fieldFuncs = [ ("domain", domain) - , ("expires", expires) - , ("path", path) ] - - domain c d = c { cookieDomain = Just d } - path c p = c { cookiePath = Just p } - expires c e = c { cookieExpires = parseExpires e } - parseExpires e = parseTime defaultTimeLocale - "%a, %d-%b-%Y %H:%M:%S GMT" - (map w2c $ S.unpack e) - - field c (k,v) = fromMaybe c (flip ($ c) v <$> lookup k fieldFuncs) - - --- unhelpfully, the spec mentions "old-style" cookies that don't have quotes --- around the value. wonderful. -pWord :: Parser ByteString -pWord = pQuotedString <|> (takeWhile ((/= ';') . w2c)) - -pAvPairs :: Parser [(ByteString, ByteString)] -pAvPairs = do - a <- pAvPair - b <- many (pSpaces *> char ';' *> pSpaces *> pAvPair) - - return $ a:b - -pAvPair :: Parser (ByteString, ByteString) -pAvPair = do - key <- pToken <* pSpaces - val <- option "" $ char '=' *> pSpaces *> pWord - - return (key,val) - -parseCookie :: ByteString -> Maybe Cookie -parseCookie = parseToCompletion pCookie - ------------------------------------------------------------------------------- --- MULTIPART/FORMDATA ------------------------------------------------------------------------------- - -parseUrlEncoded :: ByteString -> Map ByteString [ByteString] -parseUrlEncoded s = foldl' (\m (k,v) -> Map.insertWith' (++) k [v] m) - Map.empty - decoded - where - breakApart = (second (S.drop 1)) . S.break (== (c2w '=')) - - parts :: [(ByteString,ByteString)] - parts = map breakApart $ S.split (c2w '&') s - - urldecode = parseToCompletion pUrlEscaped - - decodeOne (a,b) = do - a' <- urldecode a - b' <- urldecode b - return (a',b') - - decoded = catMaybes $ map decodeOne parts - - ------------------------------------------------------------------------------- --- utility functions ------------------------------------------------------------------------------- - -strictize :: L.ByteString -> ByteString -strictize = S.concat . L.toChunks - ------------------------------------------------------------------------------- -char :: Char -> Parser Word8 -char = word8 . c2w - -- diff --git a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-Date.html b/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-Date.html deleted file mode 100644 index 40a5e91..0000000 --- a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-Date.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} - -module Snap.Internal.Http.Server.Date -( getDateString -, getLogDateString -, getCurrentDateTime) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.IORef -import Data.Time.Clock -import Data.Time.LocalTime -import Data.Time.Format -import System.IO.Unsafe -import System.Locale - - --- Here comes a dirty hack. We don't want to be wasting context switches --- building date strings, so we're only going to compute one every two --- seconds. (Approximate timestamps to within a couple of seconds are OK here, --- and we'll reduce overhead.) --- --- Note that we also don't want to wake up a potentially sleeping CPU by just --- running the computation on a timer. We'll allow client traffic to trigger --- the process. - -data DateState = DateState { - _cachedDateString :: !(IORef ByteString) - , _cachedLogString :: !(IORef ByteString) - , _cachedDate :: !(IORef UTCTime) - , _valueIsOld :: !(IORef Bool) - , _morePlease :: !(MVar ()) - , _dataAvailable :: !(MVar ()) - , _dateThread :: !(MVar ThreadId) - } - -dateState :: DateState -dateState = unsafePerformIO $ do - (s1,s2,date) <- fetchTime - bs1 <- newIORef s1 - bs2 <- newIORef s2 - dt <- newIORef date - ov <- newIORef False - th <- newEmptyMVar - mp <- newMVar () - da <- newMVar () - - let d = DateState bs1 bs2 dt ov mp da th - - t <- forkIO $ dateThread d - putMVar th t - - return d - - -fetchTime :: IO (ByteString,ByteString,UTCTime) -fetchTime = do - now <- getCurrentTime - zt <- liftM zonedTimeToLocalTime getZonedTime - return (t1 now, t2 zt, now) - where - t1 now = B.pack $ map c2w $ - formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" now - t2 now = B.pack $ map c2w $ - formatTime defaultTimeLocale "%d/%b/%Y:%H:%M:%S %z" now - -dateThread :: DateState -> IO () -dateThread ds@(DateState dateString logString time valueIsOld morePlease - dataAvailable _) = do - -- a lot of effort to make sure we don't deadlock - takeMVar morePlease - - (s1,s2,now) <- fetchTime - atomicModifyIORef dateString $ const (s1,()) - atomicModifyIORef logString $ const (s2,()) - atomicModifyIORef time $ const (now,()) - - writeIORef valueIsOld False - tryPutMVar dataAvailable () - - threadDelay 2000000 - - takeMVar dataAvailable - writeIORef valueIsOld True - - dateThread ds - -ensureFreshDate :: IO () -ensureFreshDate = block $ do - old <- readIORef $ _valueIsOld dateState - when old $ do - tryPutMVar (_morePlease dateState) () - readMVar $ _dataAvailable dateState - -getDateString :: IO ByteString -getDateString = block $ do - ensureFreshDate - readIORef $ _cachedDateString dateState - - -getLogDateString :: IO ByteString -getLogDateString = block $ do - ensureFreshDate - readIORef $ _cachedLogString dateState - - -getCurrentDateTime :: IO UTCTime -getCurrentDateTime = block $ do - ensureFreshDate - readIORef $ _cachedDate dateState - -- diff --git a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-SimpleBackend.html b/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-SimpleBackend.html deleted file mode 100644 index ae662c5..0000000 --- a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server-SimpleBackend.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - -
{-# LINE 1 "src/Snap/Internal/Http/Server/SimpleBackend.hsc" #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LINE 2 "src/Snap/Internal/Http/Server/SimpleBackend.hsc" #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server.SimpleBackend -( Backend -, BackendTerminatedException -, Connection -, TimeoutException -, debug -, bindIt -, new -, stop -, withConnection -, sendFile -, getReadEnd -, getWriteEnd -, getRemoteAddr -, getRemotePort -, getLocalAddr -, getLocalPort -) where - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Exception -import Control.Monad.Trans -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString as B -import Data.Iteratee.WrappedByteString -import Data.Typeable - -{-# LINE 38 "src/Snap/Internal/Http/Server/SimpleBackend.hsc" #-} -import Foreign.C.Types -import GHC.Conc (labelThread, forkOnIO) -import Network.Socket -import qualified Network.Socket.ByteString as SB -import qualified Network.Socket.SendFile as SF -import Prelude hiding (catch) ------------------------------------------------------------------------------- -import Snap.Internal.Debug -import Snap.Iteratee - - -data BackendTerminatedException = BackendTerminatedException - deriving (Typeable) - -instance Show BackendTerminatedException where - show (BackendTerminatedException) = "Backend terminated" - -instance Exception BackendTerminatedException - - --- foreign import ccall unsafe "set_linger" --- set_linger :: CInt -> IO () - -foreign import ccall unsafe "set_fd_timeout" - set_fd_timeout :: CInt -> IO () - - -data Backend = Backend - { _acceptSocket :: Socket } - -data Connection = Connection - { _socket :: Socket - , _remoteAddr :: ByteString - , _remotePort :: Int - , _localAddr :: ByteString - , _localPort :: Int } - - -sendFile :: Connection -> FilePath -> IO () -sendFile c fp = do - let s = _socket c - SF.sendFile s fp - - -bindIt :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> IO Socket -bindIt bindAddress bindPort = do - sock <- socket AF_INET Stream 0 - addr <- getHostAddr bindPort bindAddress - setSocketOption sock ReuseAddr 1 - bindSocket sock addr - listen sock bindPort - return sock - - -new :: Socket -- ^ value you got from bindIt - -> Int - -> IO Backend -new sock _ = do - debug $ "Backend.new: listening" - return $ Backend sock - - -stop :: Backend -> IO () -stop (Backend s) = do - debug $ "Backend.stop" - sClose s - - -data AddressNotSupportedException = AddressNotSupportedException String - deriving (Typeable) - -instance Show AddressNotSupportedException where - show (AddressNotSupportedException x) = "Address not supported: " ++ x - -instance Exception AddressNotSupportedException - - -withConnection :: Backend -> Int -> (Connection -> IO ()) -> IO () -withConnection (Backend asock) cpu proc = do - debug $ "Backend.withConnection: calling accept()" - (sock,addr) <- accept asock - - let fd = fdSocket sock - -- set linger - --set_linger fd - set_fd_timeout fd - - debug $ "Backend.withConnection: accepted connection" - debug $ "Backend.withConnection: remote: " ++ show addr - - (port,host) <- - case addr of - SockAddrInet p h -> do - h' <- inet_ntoa h - return (fromIntegral p, B.pack $ map c2w h') - x -> throwIO $ AddressNotSupportedException $ show x - - laddr <- getSocketName sock - - (lport,lhost) <- - case laddr of - SockAddrInet p h -> do - h' <- inet_ntoa h - return (fromIntegral p, B.pack $ map c2w h') - x -> throwIO $ AddressNotSupportedException $ show x - - let c = Connection sock host port lhost lport - - forkOnIO cpu $ do - labelMe $ "connHndl " ++ show fd - bracket (return c) - (\_ -> do - debug "sClose sock" - eatException $ shutdown sock ShutdownBoth - eatException $ sClose sock - eatException $ sClose sock - ) - proc - - return () - - -labelMe :: String -> IO () -labelMe s = do - tid <- myThreadId - labelThread tid s - - -eatException :: IO a -> IO () -eatException act = (act >> return ()) `catch` \(_::SomeException) -> return () - -getReadEnd :: Connection -> Enumerator IO a -getReadEnd = enumerate - - -getWriteEnd :: Connection -> Iteratee IO () -getWriteEnd = writeOut - - -getRemoteAddr :: Connection -> ByteString -getRemoteAddr = _remoteAddr - -getRemotePort :: Connection -> Int -getRemotePort = _remotePort - -getLocalAddr :: Connection -> ByteString -getLocalAddr = _localAddr - -getLocalPort :: Connection -> Int -getLocalPort = _localPort - ------------------------------------------------------------------------------- -getHostAddr :: Int - -> ByteString - -> IO SockAddr -getHostAddr p s = do - h <- if s == "*" - then return iNADDR_ANY - else inet_addr (map w2c . B.unpack $ s) - - return $ SockAddrInet (fromIntegral p) h - - - -data TimeoutException = TimeoutException - deriving (Typeable) - -instance Show TimeoutException where - show TimeoutException = "timeout" - -instance Exception TimeoutException - - -timeoutRecv :: Connection -> Int -> IO ByteString -timeoutRecv conn n = do - let sock = _socket conn - SB.recv sock n - -timeoutSend :: Connection -> ByteString -> IO () -timeoutSend conn s = do - let sock = _socket conn - SB.sendAll sock s - - -bLOCKSIZE :: Int -bLOCKSIZE = 8192 - - -enumerate :: (MonadIO m) => Connection -> Enumerator m a -enumerate = loop - where - loop conn f = do - s <- liftIO $ timeoutRecv conn bLOCKSIZE - sendOne conn f s - - sendOne conn f s = do - v <- runIter f (if B.null s - then EOF Nothing - else Chunk $ WrapBS s) - case v of - r@(Done _ _) -> return $ liftI r - (Cont k Nothing) -> loop conn k - (Cont _ (Just e)) -> return $ throwErr e - - -writeOut :: (MonadIO m) => Connection -> Iteratee m () -writeOut conn = IterateeG out - where - out c@(EOF _) = return $ Done () c - - out (Chunk s) = do - let x = unWrap s - - ee <- liftIO $ ((try $ timeoutSend conn x) - :: IO (Either SomeException ())) - - case ee of - (Left e) -> return $ Done () (EOF $ Just $ Err $ show e) - (Right _) -> return $ Cont (writeOut conn) Nothing - -- diff --git a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server.html b/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server.html deleted file mode 100644 index e51b977..0000000 --- a/static/docs/0.1.2/snap-server/src/Snap-Internal-Http-Server.html +++ /dev/null @@ -1,633 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server where - ------------------------------------------------------------------------------- -import Control.Arrow (first, second) -import Control.Monad.State.Strict -import Control.Concurrent.MVar -import Control.Exception -import Data.Char -import Data.CIByteString -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Lazy as L -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Nums.Careless.Int as Cvt -import Data.IORef -import Data.List (foldl') -import qualified Data.Map as Map -import Data.Maybe (fromJust, catMaybes, fromMaybe) -import Data.Monoid -import GHC.Conc -import Prelude hiding (catch, show, Show) -import qualified Prelude -import System.Posix.Files hiding (setFileSize) -import Text.Show.ByteString hiding (runPut) ------------------------------------------------------------------------------- -import System.FastLogger -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Internal.Http.Parser -import Snap.Iteratee hiding (foldl', head, take) -import qualified Snap.Iteratee as I - -#ifdef LIBEV -import qualified Snap.Internal.Http.Server.LibevBackend as Backend -import Snap.Internal.Http.Server.LibevBackend (debug) -#else -import qualified Snap.Internal.Http.Server.SimpleBackend as Backend -import Snap.Internal.Http.Server.SimpleBackend (debug) -#endif - -import Snap.Internal.Http.Server.Date - ------------------------------------------------------------------------------- --- | The handler has to return the request object because we have to clear the --- HTTP request body before we send the response. If the handler consumes the --- request body, it is responsible for setting @rqBody=return@ in the returned --- request (otherwise we will mess up reading the input stream). --- --- Note that we won't be bothering end users with this -- the details will be --- hidden inside the Snap monad -type ServerHandler = Request -> Iteratee IO (Request,Response) - -type ServerMonad = StateT ServerState (Iteratee IO) - -data ServerState = ServerState - { _forceConnectionClose :: Bool - , _localHostname :: ByteString - , _localAddress :: ByteString - , _localPort :: Int - , _remoteAddr :: ByteString - , _remotePort :: Int - , _logAccess :: Request -> Response -> IO () - , _logError :: ByteString -> IO () - } - - ------------------------------------------------------------------------------- -runServerMonad :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> (Request -> Response -> IO ()) -- ^ access log function - -> (ByteString -> IO ()) -- ^ error log function - -> ServerMonad a -- ^ monadic action to run - -> Iteratee IO a -runServerMonad lh lip lp rip rp la le m = evalStateT m st - where - st = ServerState False lh lip lp rip rp la le - - - ------------------------------------------------------------------------------- --- input/output - - ------------------------------------------------------------------------------- -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the access log - -> Maybe FilePath -- ^ path to the error log - -> ServerHandler -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alogPath elogPath handler = - withLoggers alogPath elogPath - (\(alog, elog) -> spawnAll alog elog) - - where - spawnAll alog elog = do - let n = numCapabilities - bracket (spawn n) - (\xs -> do - logE elog "Server.httpServe: SHUTDOWN" - mapM_ (Backend.stop . fst) xs - logE elog "Server.httpServe: BACKEND STOPPED") - (runAll alog elog) - - - runAll alog elog xs = do - mapM_ f $ xs `zip` [0..] - mapM_ (takeMVar . snd) xs - where - f ((backend,mvar),cpu) = - forkOnIO cpu $ do - labelMe $ map w2c $ S.unpack $ - S.concat ["accThread ", l2s $ show cpu] - (try $ (goooo alog elog backend cpu)) :: IO (Either SomeException ()) - putMVar mvar () - - goooo alog elog backend cpu = - let loop = go alog elog backend cpu >> loop - in loop - - maybeSpawnLogger = maybe (return Nothing) $ (liftM Just) . newLogger - - withLoggers afp efp = - bracket (do alog <- maybeSpawnLogger afp - elog <- maybeSpawnLogger efp - return (alog, elog)) - (\(alog, elog) -> do - threadDelay 1000000 - maybe (return ()) stopLogger alog - maybe (return ()) stopLogger elog) - - labelMe :: String -> IO () - labelMe s = do - tid <- myThreadId - labelThread tid s - - spawn n = do - sock <- Backend.bindIt bindAddress bindPort - backends <- mapM (Backend.new sock) $ [0..(n-1)] - mvars <- replicateM n newEmptyMVar - - return (backends `zip` mvars) - - - runOne alog elog backend cpu = Backend.withConnection backend cpu $ \conn -> do - debug "Server.httpServe.runOne: entered" - let readEnd = Backend.getReadEnd conn - writeEnd <- I.bufferIteratee $ Backend.getWriteEnd conn - - let raddr = Backend.getRemoteAddr conn - let rport = Backend.getRemotePort conn - let laddr = Backend.getLocalAddr conn - let lport = Backend.getLocalPort conn - - runHTTP localHostname laddr lport raddr rport - alog elog readEnd writeEnd (Backend.sendFile conn) - handler - - debug "Server.httpServe.runHTTP: finished" - - - go alog elog backend cpu = runOne alog elog backend cpu - `catches` - [ Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: AsyncException) -> do - logE elog $ - S.concat [ "Server.httpServe.go: got async exception, " - , "terminating:\n", bshow e ] - throwIO e - - , Handler $ \(e :: Backend.BackendTerminatedException) -> do - logE elog $ "Server.httpServe.go: got backend terminated, waiting for cleanup" - throwIO e - - , Handler $ \(e :: IOException) -> do - logE elog $ S.concat [ "Server.httpServe.go: got io exception: " - , bshow e ] - - , Handler $ \(e :: SomeException) -> do - logE elog $ S.concat [ - "Server.httpServe.go: got someexception: " - , bshow e ] - return () ] - ------------------------------------------------------------------------------- -debugE :: (MonadIO m) => ByteString -> m () -debugE s = debug $ "Server: " ++ (map w2c $ S.unpack s) - - ------------------------------------------------------------------------------- -logE :: Maybe Logger -> ByteString -> IO () -logE elog = maybe debugE (\l s -> debugE s >> logE' l s) elog - -logE' :: Logger -> ByteString -> IO () -logE' logger s = (timestampedLogEntry s) >>= logMsg logger - - -bshow :: (Prelude.Show a) => a -> ByteString -bshow = toBS . Prelude.show - ------------------------------------------------------------------------------- -logA ::Maybe Logger -> Request -> Response -> IO () -logA alog = maybe (\_ _ -> return ()) logA' alog - -logA' :: Logger -> Request -> Response -> IO () -logA' logger req rsp = do - let hdrs = rqHeaders req - let host = rqRemoteAddr req - let user = Nothing -- TODO we don't do authentication yet - let (v, v') = rqVersion req - let ver = S.concat [ "HTTP/", bshow v, ".", bshow v' ] - let method = toBS $ Prelude.show (rqMethod req) - let reql = S.intercalate " " [ method, rqURI req, ver ] - let status = rspStatus rsp - let cl = rspContentLength rsp - let referer = maybe Nothing (Just . head) $ Map.lookup "referer" hdrs - let userAgent = maybe "-" head $ Map.lookup "user-agent" hdrs - - msg <- combinedLogEntry host user reql status cl referer userAgent - logMsg logger msg - - ------------------------------------------------------------------------------- -runHTTP :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> Maybe Logger -- ^ access logger - -> Maybe Logger -- ^ error logger - -> Enumerator IO () -- ^ read end of socket - -> Iteratee IO () -- ^ write end of socket - -> (FilePath -> IO ()) -- ^ sendfile end - -> ServerHandler -- ^ handler procedure - -> IO () -runHTTP lh lip lp rip rp alog elog readEnd writeEnd onSendFile handler = - go `catches` [ Handler $ \(e :: AsyncException) -> do - throwIO e - - , Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: SomeException) -> - logE elog $ toBS $ Prelude.show e ] - - where - go = do - let iter = runServerMonad lh lip lp rip rp (logA alog) (logE elog) $ - httpSession writeEnd onSendFile handler - readEnd iter >>= run - - ------------------------------------------------------------------------------- -sERVER_HEADER :: [ByteString] -sERVER_HEADER = ["Snap/0.pre-1"] - - ------------------------------------------------------------------------------- -logAccess :: Request -> Response -> ServerMonad () -logAccess req rsp = gets _logAccess >>= (\l -> liftIO $ l req rsp) - ------------------------------------------------------------------------------- -logError :: ByteString -> ServerMonad () -logError s = gets _logError >>= (\l -> liftIO $ l s) - ------------------------------------------------------------------------------- --- | Runs an HTTP session. -httpSession :: Iteratee IO () -- ^ write end of socket - -> (FilePath -> IO ()) -- ^ sendfile continuation - -> ServerHandler -- ^ handler procedure - -> ServerMonad () -httpSession writeEnd onSendFile handler = do - liftIO $ debug "Server.httpSession: entered" - mreq <- receiveRequest - - case mreq of - (Just req) -> do - (req',rspOrig) <- lift $ handler req - let rspTmp = rspOrig { rspHttpVersion = rqVersion req } - checkConnectionClose (rspHttpVersion rspTmp) (rspHeaders rspTmp) - - cc <- gets _forceConnectionClose - let rsp = if cc - then (setHeader "Connection" "close" rspTmp) - else rspTmp - - - liftIO $ debug "Server.httpSession: handled, skipping request body" - srqEnum <- liftIO $ readIORef $ rqBody req' - let (SomeEnumerator rqEnum) = srqEnum - lift $ joinIM $ rqEnum skipToEof - liftIO $ debug "Server.httpSession: request body skipped, sending response" - - date <- liftIO getDateString - let ins = (Map.insert "Date" [date] . Map.insert "Server" sERVER_HEADER) - let rsp' = updateHeaders ins rsp - (bytesSent,_) <- sendResponse rsp' writeEnd onSendFile - - maybe (logAccess req rsp') - (\_ -> logAccess req $ setContentLength bytesSent rsp') - (rspContentLength rsp') - - if cc - then return () - else httpSession writeEnd onSendFile handler - - Nothing -> return () - ------------------------------------------------------------------------------- -receiveRequest :: ServerMonad (Maybe Request) -receiveRequest = do - mreq <- lift parseRequest - - case mreq of - (Just ireq) -> do - req' <- toRequest ireq - setEnumerator req' - req <- parseForm req' - checkConnectionClose (rqVersion req) (rqHeaders req) - return $ Just req - - Nothing -> return Nothing - - - where - -- check: did the client specify "transfer-encoding: chunked"? then we have - -- to honor that. - -- - -- otherwise: check content-length header. if set: only take N bytes from - -- the read end of the socket - -- - -- if no content-length and no chunked encoding, enumerate the entire - -- socket and close afterwards - setEnumerator :: Request -> ServerMonad () - setEnumerator req = - if isChunked - then liftIO $ writeIORef (rqBody req) - (SomeEnumerator readChunkedTransferEncoding) - else maybe noContentLength hasContentLength mbCL - - where - isChunked = maybe False - ((== ["chunked"]) . map toCI) - (Map.lookup "transfer-encoding" hdrs) - - hasContentLength :: Int -> ServerMonad () - hasContentLength l = do - liftIO $ writeIORef (rqBody req) - (SomeEnumerator e) - where - e :: Enumerator IO a - e = return . joinI . I.take l - - noContentLength :: ServerMonad () - noContentLength = - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . joinI . I.take 0 ) - - - hdrs = rqHeaders req - mbCL = Map.lookup "content-length" hdrs >>= return . Cvt.int . head - - - parseForm :: Request -> ServerMonad Request - parseForm req = if doIt then getIt else return req - where - doIt = mbCT == Just "application/x-www-form-urlencoded" - mbCT = liftM head $ Map.lookup "content-type" (rqHeaders req) - - maximumPOSTBodySize :: Int - maximumPOSTBodySize = 10*1024*1024 - - getIt :: ServerMonad Request - getIt = do - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - let i = joinI $ takeNoMoreThan maximumPOSTBodySize stream2stream - iter <- liftIO $ enum i - body <- lift iter - let newParams = parseUrlEncoded $ strictize $ fromWrap body - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . I.joinI . I.take 0) - return $ req { rqParams = rqParams req `mappend` newParams } - - - toRequest (IRequest method uri version kvps) = do - localAddr <- gets _localAddress - localPort <- gets _localPort - remoteAddr <- gets _remoteAddr - remotePort <- gets _remotePort - localHostname <- gets _localHostname - - let (serverName, serverPort) = fromMaybe - (localHostname, localPort) - (liftM (parseHost . head) - (Map.lookup "host" hdrs)) - - -- will override in "setEnumerator" - enum <- liftIO $ newIORef $ SomeEnumerator return - - - return $ Request serverName - serverPort - remoteAddr - remotePort - localAddr - localPort - localHostname - isSecure - hdrs - enum - mbContentLength - method - version - cookies - snapletPath - pathInfo - contextPath - uri - queryString - params - - where - snapletPath = "" -- TODO: snaplets in v0.2 - - dropLeadingSlash s = maybe s f mbS - where - f (a,s') = if a == c2w '/' then s' else s - mbS = S.uncons s - - isSecure = False - - hdrs = toHeaders kvps - - mbContentLength = liftM (Cvt.int . head) $ - Map.lookup "content-length" hdrs - - cookies = maybe [] - (catMaybes . map parseCookie) - (Map.lookup "set-cookie" hdrs) - - contextPath = "/" - - parseHost h = (a, Cvt.int (S.drop 1 b)) - where - (a,b) = S.break (== (c2w ':')) h - - params = parseUrlEncoded queryString - - (pathInfo, queryString) = first dropLeadingSlash . second (S.drop 1) $ - S.break (== (c2w '?')) uri - - ------------------------------------------------------------------------------- --- Response must be well-formed here -sendResponse :: Response - -> Iteratee IO a - -> (FilePath -> IO a) - -> ServerMonad (Int,a) -sendResponse rsp' writeEnd onSendFile = do - rsp <- fixupResponse rsp' - let !headerString = mkHeaderString rsp - - (!x,!bs) <- case (rspBody rsp) of - (Enum e) -> liftIO $ whenEnum headerString e - (SendFile f) -> liftIO $ whenSendFile headerString rsp f - - return $! (bs,x) - - where - whenEnum hs e = do - let enum = enumBS hs >. e - let hl = S.length hs - (x,bs) <- liftIO $ enum (countBytes writeEnd) >>= run - - return (x, bs-hl) - - whenSendFile hs r f = do - -- guaranteed to have a content length here. - enumBS hs writeEnd >>= run - - let !cl = fromJust $ rspContentLength r - x <- onSendFile f - return (x, cl) - - (major,minor) = rspHttpVersion rsp' - - - fmtHdrs hdrs = - {-# SCC "fmtHdrs" #-} - concat xs - where - xs = map f $ Map.toList hdrs - - f (k, ys) = map (g k) ys - - g k y = S.concat [ unCI k, ": ", y, "\r\n" ] - - - noCL :: Response -> ServerMonad Response - noCL r = - {-# SCC "noCL" #-} - do - -- are we in HTTP/1.1? - let sendChunked = (rspHttpVersion r) == (1,1) - if sendChunked - then do - let r' = setHeader "Transfer-Encoding" "chunked" r - let e = writeChunkedTransferEncoding $ rspBodyToEnum $ rspBody r - return $ r' { rspBody = Enum e } - - else do - -- HTTP/1.0 and no content-length? We'll have to close the - -- socket. - modify $! \s -> s { _forceConnectionClose = True } - return $ setHeader "Connection" "close" r - - - hasCL :: Int -> Response -> ServerMonad Response - hasCL cl r = - {-# SCC "hasCL" #-} - do - -- set the content-length header - let r' = setHeader "Content-Length" (l2s $ show cl) r - let b = case (rspBody r') of - (Enum e) -> Enum (i e) - (SendFile f) -> SendFile f - - return $ r' { rspBody = b } - - where - i :: Enumerator IO a -> Enumerator IO a - i enum iter = enum (joinI $ takeExactly cl iter) - - - setFileSize :: FilePath -> Response -> ServerMonad Response - setFileSize fp r = - {-# SCC "setFileSize" #-} - do - fs <- liftM fromEnum $ liftIO $ getFileSize fp - return $ r { rspContentLength = Just fs } - - - fixupResponse :: Response -> ServerMonad Response - fixupResponse r = - {-# SCC "fixupResponse" #-} - do - let r' = updateHeaders (Map.delete "Content-Length") r - r'' <- case (rspBody r') of - (Enum _) -> return r' - (SendFile f) -> setFileSize f r' - case (rspContentLength r'') of - Nothing -> noCL r'' - (Just sz) -> hasCL sz r'' - - - bsshow = l2s . show - - - mkHeaderString :: Response -> ByteString - mkHeaderString r = - {-# SCC "mkHeaderString" #-} - S.concat $ concat [hl, hdr, eol] - where - hl = [ "HTTP/" - , bsshow major - , "." - , bsshow minor - , " " - , bsshow $ rspStatus r - , " " - , rspStatusReason r - , "\r\n" ] - - hdr = fmtHdrs $ headers r - - eol = ["\r\n"] - - ------------------------------------------------------------------------------- -checkConnectionClose :: (Int, Int) -> Headers -> ServerMonad () -checkConnectionClose ver hdrs = - -- For HTTP/1.1: - -- if there is an explicit Connection: close, close the socket. - -- For HTTP/1.0: - -- if there is no explicit Connection: Keep-Alive, close the socket. - if (ver == (1,1) && l == Just ["close"]) || - (ver == (1,0) && l /= Just ["Keep-Alive"]) - then modify $ \s -> s { _forceConnectionClose = True } - else return () - where - l = liftM (map tl) $ Map.lookup "Connection" hdrs - tl = S.map (c2w . toLower . w2c) - - ------------------------------------------------------------------------------- --- FIXME: whitespace-trim the values here. -toHeaders :: [(ByteString,ByteString)] -> Headers -toHeaders kvps = foldl' f Map.empty kvps' - where - kvps' = map (first toCI . second (:[])) kvps - f m (k,v) = Map.insertWith' (flip (++)) k v m - - ------------------------------------------------------------------------------- -getFileSize :: FilePath -> IO FileOffset -getFileSize fp = liftM fileSize $ getFileStatus fp - - -l2s :: L.ByteString -> S.ByteString -l2s = S.concat . L.toChunks - - -toBS :: String -> ByteString -toBS = S.pack . map c2w -- diff --git a/static/docs/0.1.2/snap-server/src/System-FastLogger.html b/static/docs/0.1.2/snap-server/src/System-FastLogger.html deleted file mode 100644 index 0a9d919..0000000 --- a/static/docs/0.1.2/snap-server/src/System-FastLogger.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module System.FastLogger -( Logger -, timestampedLogEntry -, combinedLogEntry -, newLogger -, logMsg -, stopLogger -) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Internal (c2w) -import Data.DList (DList) -import qualified Data.DList as D -import Data.IORef -import Data.Maybe -import Data.Serialize.Put -import Data.Time.Clock -import Prelude hiding (catch, show) -import qualified Prelude -import System.IO -import Text.Show.ByteString hiding (runPut) - -import Snap.Internal.Http.Server.Date - - --- | Holds the state for a logger. -data Logger = Logger - { _queuedMessages :: !(IORef (DList ByteString)) - , _dataWaiting :: !(MVar ()) - , _loggerPath :: !(FilePath) - , _loggingThread :: !(MVar ThreadId) } - - --- | Creates a new logger, logging to the given file. If the file argument is --- \"-\", then log to stdout; if it's \"stderr\" then we log to stderr, --- otherwise we log to a regular file in append mode. The file is closed and --- re-opened every 15 minutes to facilitate external log rotation. -newLogger :: FilePath -> IO Logger -newLogger fp = do - q <- newIORef D.empty - dw <- newEmptyMVar - th <- newEmptyMVar - - let lg = Logger q dw fp th - - tid <- forkIO $ loggingThread lg - putMVar th tid - - return lg - --- | Prepares a log message with the time prepended. -timestampedLogEntry :: ByteString -> IO ByteString -timestampedLogEntry msg = do - timeStr <- getLogDateString - - return $ runPut $ do - putWord8 $ c2w '[' - putByteString timeStr - putByteString "] " - putByteString msg - - --- | Prepares a log message in \"combined\" format. -combinedLogEntry :: ByteString -- ^ remote host - -> Maybe ByteString -- ^ remote user - -> ByteString -- ^ request line (up to you to ensure - -- there are no quotes in here) - -> Int -- ^ status code - -> Maybe Int -- ^ num bytes sent - -> Maybe ByteString -- ^ referer (up to you to ensure - -- there are no quotes in here) - -> ByteString -- ^ user agent (up to you to ensure - -- there are no quotes in here) - -> IO ByteString -combinedLogEntry host mbUser req status mbNumBytes mbReferer userAgent = do - let user = fromMaybe "-" mbUser - let numBytes = maybe "-" (\s -> strict $ show s) mbNumBytes - let referer = maybe "-" (\s -> S.concat ["\"", s, "\""]) mbReferer - - timeStr <- getLogDateString - - let p = [ host - , " - " - , user - , " [" - , timeStr - , "] \"" - , req - , "\" " - , strict $ show status - , " " - , numBytes - , " " - , referer - , " \"" - , userAgent - , "\"" ] - - return $ S.concat p - - - where - strict = S.concat . L.toChunks - - --- | Sends out a log message verbatim with a newline appended. Note: --- if you want a fancy log message you'll have to format it yourself --- (or use 'combinedLogEntry'). -logMsg :: Logger -> ByteString -> IO () -logMsg lg s = do - let s' = S.snoc s '\n' - atomicModifyIORef (_queuedMessages lg) $ \d -> (D.snoc d s',()) - tryPutMVar (_dataWaiting lg) () >> return () - - -loggingThread :: Logger -> IO () -loggingThread (Logger queue notifier filePath _) = do - initialize >>= go - - where - openIt = if filePath == "-" - then return stdout - else if filePath == "stderr" - then return stderr - else openFile filePath AppendMode - - closeIt h = if filePath == "-" || filePath == "stderr" - then return () - else hClose h - - go (href, lastOpened) = - (loop (href, lastOpened)) - `catches` - [ Handler $ \(_::AsyncException) -> killit (href, lastOpened) - , Handler $ \(e::SomeException) -> do - hPutStrLn stderr $ "logger got exception: " ++ Prelude.show e - threadDelay 20000000 - go (href, lastOpened) ] - - - initialize = do - lh <- openIt - href <- newIORef lh - t <- getCurrentTime - tref <- newIORef t - return (href, tref) - - - killit (href, lastOpened) = do - flushIt (href, lastOpened) - h <- readIORef href - closeIt h - - - flushIt (href, lastOpened) = do - dl <- atomicModifyIORef queue $ \x -> (D.empty,x) - - let msgs = D.toList dl - let s = L.fromChunks msgs - h <- readIORef href - L.hPut h s - hFlush h - - -- close the file every 15 minutes (for log rotation) - t <- getCurrentTime - old <- readIORef lastOpened - - if diffUTCTime t old > 900 - then do - closeIt h - openIt >>= writeIORef href - writeIORef lastOpened t - else return () - - - loop (href, lastOpened) = do - -- wait on the notification mvar - _ <- takeMVar notifier - - -- grab the queued messages and write them out - flushIt (href, lastOpened) - - -- at least five seconds between log dumps - threadDelay 5000000 - - loop (href, lastOpened) - - --- | Kills a logger thread, causing any unwritten contents to be --- flushed out to disk -stopLogger :: Logger -> IO () -stopLogger lg = withMVar (_loggingThread lg) killThread -- diff --git a/static/docs/0.1.2/snap-server/src/hscolour.css b/static/docs/0.1.2/snap-server/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.1.2/snap-server/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.2.1/heist/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.2.1/heist/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index b53b781..0000000 --- a/static/docs/0.2.1/heist/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,225 +0,0 @@ - - -
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the apply splice. - | |||||
| |||||
Default attribute name for the apply tag. - | |||||
| |||||
Implementation of the apply splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the bind splice. - | |||||
| |||||
Default attribute name for the bind tag. - | |||||
| |||||
Implementation of the bind splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the ignore splice. - | |||||
| |||||
The ignore tag and everything it surrounds disappears in the - rendered output. - | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Default name for the markdown splice. - | |||||||||||||
| |||||||||||||
Implementation of the markdown splice. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
Modifies a TemplateState to include a static tag. - | |||||
| |||||
Clears the static tag state. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Documentation | |||||
module Text.Templating.Heist.Splices.Apply | |||||
module Text.Templating.Heist.Splices.Bind | |||||
module Text.Templating.Heist.Splices.Ignore | |||||
module Text.Templating.Heist.Splices.Markdown | |||||
module Text.Templating.Heist.Splices.Static | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core definitions for the Heist template system. - The Heist template system is based on XML/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - The most important concept in Heist is the Splice. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. Splice is implemented as a type synonym type Splice m = - TemplateMonad m [Node], and TemplateMonad has a function getParamNode - that lets you get the input node. - Suppose you have a place on your page where you want to display a link with - the text "Logout username" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - getUser :: MyAppMonad (Maybe ByteString) that gets the current user. - You can implement this functionality with a Splice as follows: - - import Text.XML.Expat.Tree - - link :: ByteString -> ByteString -> Node - link target text = X.Element "a" [("href", target)] [X.Text text] - - loginLink :: Node - loginLink = link "/login" "Login" - - logoutLink :: ByteString -> Node - logoutLink user = link "/logout" (B.append "Logout " user) - - loginLogoutSplice :: Splice MyAppMonad - loginLogoutSplice = do - user <- lift getUser - return $ [maybe loginLink logoutLink user] - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the TemplateState data structure. The - following code demonstrates how this splice would be used. - mySplices = [ ("loginLogout", loginLogoutSplice) ] - - main = do - ets <- loadTemplates "templates" $ - foldr (uncurry bindSplice) emptyTemplateState mySplices - let ts = either error id ets - t <- runMyAppMonad $ renderTemplate ts "index" - print $ maybe "Page not found" id t - Here we build up our TemplateState by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final TemplateState wrapped in an Either to handle - errors. Then we use this TemplateState to render our templates. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Types - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist templates are XML documents. The hexpat library is polymorphic over - the type of strings, so here we define a Node alias to fix the string - types of the tag names and tag bodies to ByteString. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Splice is a TemplateMonad computation that returns [Node]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Template is a forest of XML nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions and declarations on TemplateState values - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a template to the template state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty template state, with Heist's default splices (<bind> and - <apply>) mapped. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Convenience function for looking up a splice. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the templateMap in a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Traverses the specified directory structure and builds a - TemplateState by loading all the files with a .tpl extension. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hook functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist hooks allow you to modify templates when they are loaded and before - and after they are run. Every time you call one of the addAbcHook - functions the hook is added to onto the processing pipeline. The hooks - processes the template in the order that they were added to the - TemplateState. - The pre-run and post-run hooks are run before and after every template is - run/rendered. You should be careful what code you put in these hooks - because it can significantly affect the performance of your site. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds an on-load hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a pre-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a post-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TemplateMonad functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Stops the recursive processing of splices. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the node currently being processed. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Performs splice processing on a list of nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the current context - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for running splices and templates - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name in the supplied TemplateState and runs - it in the underlying monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name evaluates it. Same as runTemplate except it - runs in TemplateMonad instead of m. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Renders a template from the specified TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Binds a list of constant string splices - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Misc functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a template in the underlying monad. Similar to runSplice - except that templates don't require a Node as a parameter. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reads an XML document from disk. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a TemplateState to include a static tag. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||||||||||
heist-0.1.2: An xhtml templating system | |||||||||||||||||||||||||||||||||||||||
An xhtml templating system - | |||||||||||||||||||||||||||||||||||||||
Modules | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE OverloadedStrings #-} -module Text.Templating.Heist.Constants where - -import Data.ByteString.Char8 (ByteString) -import qualified Data.Map as Map -import Data.Map (Map) - -htmlEntityLookupTable :: Map ByteString ByteString -htmlEntityLookupTable = Map.fromList [ - ("acute" , "\xc2\xb4") - , ("cedil" , "\xc2\xb8") - , ("circ" , "\xcb\x86") - , ("macr" , "\xc2\xaf") - , ("middot" , "\xc2\xb7") - , ("tilde" , "\xcb\x9c") - , ("uml" , "\xc2\xa8") - , ("Aacute" , "\xc3\x81") - , ("aacute" , "\xc3\xa1") - , ("Acirc" , "\xc3\x82") - , ("acirc" , "\xc3\xa2") - , ("AElig" , "\xc3\x86") - , ("aelig" , "\xc3\xa6") - , ("Agrave" , "\xc3\x80") - , ("agrave" , "\xc3\xa0") - , ("Aring" , "\xc3\x85") - , ("aring" , "\xc3\xa5") - , ("Atilde" , "\xc3\x83") - , ("atilde" , "\xc3\xa3") - , ("Auml" , "\xc3\x84") - , ("auml" , "\xc3\xa4") - , ("Ccedil" , "\xc3\x87") - , ("ccedil" , "\xc3\xa7") - , ("Eacute" , "\xc3\x89") - , ("eacute" , "\xc3\xa9") - , ("Ecirc" , "\xc3\x8a") - , ("ecirc" , "\xc3\xaa") - , ("Egrave" , "\xc3\x88") - , ("egrave" , "\xc3\xa8") - , ("ETH" , "\xc3\x90") - , ("eth" , "\xc3\xb0") - , ("Euml" , "\xc3\x8b") - , ("euml" , "\xc3\xab") - , ("Iacute" , "\xc3\x8d") - , ("iacute" , "\xc3\xad") - , ("Icirc" , "\xc3\x8e") - , ("icirc" , "\xc3\xae") - , ("Igrave" , "\xc3\x8c") - , ("igrave" , "\xc3\xac") - , ("Iuml" , "\xc3\x8f") - , ("iuml" , "\xc3\xaf") - , ("Ntilde" , "\xc3\x91") - , ("ntilde" , "\xc3\xb1") - , ("Oacute" , "\xc3\x93") - , ("oacute" , "\xc3\xb3") - , ("Ocirc" , "\xc3\x94") - , ("ocirc" , "\xc3\xb4") - , ("OElig" , "\xc5\x92") - , ("oelig" , "\xc5\x93") - , ("Ograve" , "\xc3\x92") - , ("ograve" , "\xc3\xb2") - , ("Oslash" , "\xc3\x98") - , ("oslash" , "\xc3\xb8") - , ("Otilde" , "\xc3\x95") - , ("otilde" , "\xc3\xb5") - , ("Ouml" , "\xc3\x96") - , ("ouml" , "\xc3\xb6") - , ("Scaron" , "\xc5\xa0") - , ("scaron" , "\xc5\xa1") - , ("szlig" , "\xc3\x9f") - , ("THORN" , "\xc3\x9e") - , ("thorn" , "\xc3\xbe") - , ("Uacute" , "\xc3\x9a") - , ("uacute" , "\xc3\xba") - , ("Ucirc" , "\xc3\x9b") - , ("ucirc" , "\xc3\xbb") - , ("Ugrave" , "\xc3\x99") - , ("ugrave" , "\xc3\xb9") - , ("Uuml" , "\xc3\x9c") - , ("uuml" , "\xc3\xbc") - , ("Yacute" , "\xc3\x9d") - , ("yacute" , "\xc3\xbd") - , ("yuml" , "\xc3\xbf") - , ("Yuml" , "\xc5\xb8") - , ("cent" , "\xc2\xa2") - , ("curren" , "\xc2\xa4") - , ("euro" , "\xe2\x82\xac") - , ("pound" , "\xc2\xa3") - , ("yen" , "\xc2\xa5") - , ("brvbar" , "\xc2\xa6") - , ("bull" , "\xe2\x80\xa2") - , ("copy" , "\xc2\xa9") - , ("dagger" , "\xe2\x80\xa0") - , ("Dagger" , "\xe2\x80\xa1") - , ("frasl" , "\xe2\x81\x84") - , ("hellip" , "\xe2\x80\xa6") - , ("iexcl" , "\xc2\xa1") - , ("image" , "\xe2\x84\x91") - , ("iquest" , "\xc2\xbf") - , ("mdash" , "\xe2\x80\x94") - , ("ndash" , "\xe2\x80\x93") - , ("not" , "\xc2\xac") - , ("oline" , "\xe2\x80\xbe") - , ("ordf" , "\xc2\xaa") - , ("ordm" , "\xc2\xba") - , ("para" , "\xc2\xb6") - , ("permil" , "\xe2\x80\xb0") - , ("prime" , "\xe2\x80\xb2") - , ("Prime" , "\xe2\x80\xb3") - , ("real" , "\xe2\x84\x9c") - , ("reg" , "\xc2\xae") - , ("sect" , "\xc2\xa7") - , ("shy" , "\173") - , ("sup1" , "\xc2\xb9") - , ("trade" , "\xe2\x84\xa2") - , ("weierp" , "\xe2\x84\x98") - , ("bdquo" , "\xe2\x80\x9e") - , ("laquo" , "\xc2\xab") - , ("ldquo" , "\xe2\x80\x9c") - , ("lsaquo" , "\xe2\x80\xb9") - , ("lsquo" , "\xe2\x80\x98") - , ("raquo" , "\xc2\xbb") - , ("rdquo" , "\xe2\x80\x9d") - , ("rsaquo" , "\xe2\x80\xba") - , ("rsquo" , "\xe2\x80\x99") - , ("sbquo" , "\xe2\x80\x9a") - , ("emsp" , "\xe2\x80\x83") - , ("ensp" , "\xe2\x80\x82") - , ("nbsp" , "\x20") - , ("thinsp" , "\xe2\x80\x89") - , ("zwj" , "\xe2\x80\x8d") - , ("zwnj" , "\xe2\x80\x8c") - , ("deg" , "\xc2\xb0") - , ("divide" , "\xc3\xb7") - , ("frac12" , "\xc2\xbd") - , ("frac14" , "\xc2\xbc") - , ("frac34" , "\xc2\xbe") - , ("ge" , "\xe2\x89\xa5") - , ("le" , "\xe2\x89\xa4") - , ("minus" , "\xe2\x88\x92") - , ("sup2" , "\xc2\xb2") - , ("sup3" , "\xc2\xb3") - , ("times" , "\xc3\x97") - , ("alefsym" , "\xe2\x84\xb5") - , ("and" , "\xe2\x88\xa7") - , ("ang" , "\xe2\x88\xa0") - , ("asymp" , "\xe2\x89\x88") - , ("cap" , "\xe2\x88\xa9") - , ("cong" , "\xe2\x89\x85") - , ("cup" , "\xe2\x88\xaa") - , ("empty" , "\xe2\x88\x85") - , ("equiv" , "\xe2\x89\xa1") - , ("exist" , "\xe2\x88\x83") - , ("fnof" , "\xc6\x92") - , ("forall" , "\xe2\x88\x80") - , ("infin" , "\xe2\x88\x9e") - , ("int" , "\xe2\x88\xab") - , ("isin" , "\xe2\x88\x88") - , ("lang" , "\xe3\x80\x88") - , ("lceil" , "\xe2\x8c\x88") - , ("lfloor" , "\xe2\x8c\x8a") - , ("lowast" , "\xe2\x88\x97") - , ("micro" , "\xc2\xb5") - , ("nabla" , "\xe2\x88\x87") - , ("ne" , "\xe2\x89\xa0") - , ("ni" , "\xe2\x88\x8b") - , ("notin" , "\xe2\x88\x89") - , ("nsub" , "\xe2\x8a\x84") - , ("oplus" , "\xe2\x8a\x95") - , ("or" , "\xe2\x88\xa8") - , ("otimes" , "\xe2\x8a\x97") - , ("part" , "\xe2\x88\x82") - , ("perp" , "\xe2\x8a\xa5") - , ("plusmn" , "\xc2\xb1") - , ("prod" , "\xe2\x88\x8f") - , ("prop" , "\xe2\x88\x9d") - , ("radic" , "\xe2\x88\x9a") - , ("rang" , "\xe3\x80\x89") - , ("rceil" , "\xe2\x8c\x89") - , ("rfloor" , "\xe2\x8c\x8b") - , ("sdot" , "\xe2\x8b\x85") - , ("sim" , "\xe2\x88\xbc") - , ("sub" , "\xe2\x8a\x82") - , ("sube" , "\xe2\x8a\x86") - , ("sum" , "\xe2\x88\x91") - , ("sup" , "\xe2\x8a\x83") - , ("supe" , "\xe2\x8a\x87") - , ("there4" , "\xe2\x88\xb4") - , ("Alpha" , "\xce\x91") - , ("alpha" , "\xce\xb1") - , ("Beta" , "\xce\x92") - , ("beta" , "\xce\xb2") - , ("Chi" , "\xce\xa7") - , ("chi" , "\xcf\x87") - , ("Delta" , "\xce\x94") - , ("delta" , "\xce\xb4") - , ("Epsilon" , "\xce\x95") - , ("epsilon" , "\xce\xb5") - , ("Eta" , "\xce\x97") - , ("eta" , "\xce\xb7") - , ("Gamma" , "\xce\x93") - , ("gamma" , "\xce\xb3") - , ("Iota" , "\xce\x99") - , ("iota" , "\xce\xb9") - , ("Kappa" , "\xce\x9a") - , ("kappa" , "\xce\xba") - , ("Lambda" , "\xce\x9b") - , ("lambda" , "\xce\xbb") - , ("Mu" , "\xce\x9c") - , ("mu" , "\xce\xbc") - , ("Nu" , "\xce\x9d") - , ("nu" , "\xce\xbd") - , ("Omega" , "\xce\xa9") - , ("omega" , "\xcf\x89") - , ("Omicron" , "\xce\x9f") - , ("omicron" , "\xce\xbf") - , ("Phi" , "\xce\xa6") - , ("phi" , "\xcf\x86") - , ("Pi" , "\xce\xa0") - , ("pi" , "\xcf\x80") - , ("piv" , "\xcf\x96") - , ("Psi" , "\xce\xa8") - , ("psi" , "\xcf\x88") - , ("Rho" , "\xce\xa1") - , ("rho" , "\xcf\x81") - , ("Sigma" , "\xce\xa3") - , ("sigma" , "\xcf\x83") - , ("sigmaf" , "\xcf\x82") - , ("Tau" , "\xce\xa4") - , ("tau" , "\xcf\x84") - , ("Theta" , "\xce\x98") - , ("theta" , "\xce\xb8") - , ("thetasym" , "\xcf\x91") - , ("upsih" , "\xcf\x92") - , ("Upsilon" , "\xce\xa5") - , ("upsilon" , "\xcf\x85") - , ("Xi" , "\xce\x9e") - , ("xi" , "\xce\xbe") - , ("Zeta" , "\xce\x96") - , ("zeta" , "\xce\xb6") - , ("crarr" , "\xe2\x86\xb5") - , ("darr" , "\xe2\x86\x93") - , ("dArr" , "\xe2\x87\x93") - , ("harr" , "\xe2\x86\x94") - , ("hArr" , "\xe2\x87\x94") - , ("larr" , "\xe2\x86\x90") - , ("lArr" , "\xe2\x87\x90") - , ("rarr" , "\xe2\x86\x92") - , ("rArr" , "\xe2\x87\x92") - , ("uarr" , "\xe2\x86\x91") - , ("uArr" , "\xe2\x87\x91") - , ("clubs" , "\xe2\x99\xa3") - , ("diams" , "\xe2\x99\xa6") - , ("hearts" , "\xe2\x99\xa5") - , ("spades" , "\xe2\x99\xa0") - , ("loz" , "\xe2\x97\x8a") ] -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Internal.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Internal.html deleted file mode 100644 index 1d76a70..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Internal.html +++ /dev/null @@ -1,513 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Text.Templating.Heist.Internal where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad.CatchIO -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as L -import Data.Either -import qualified Data.Foldable as F -import Data.List -import qualified Data.Map as Map -import Data.Map (Map) -import Prelude hiding (catch) -import System.Directory.Tree hiding (name) -import Text.XML.Expat.Format -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Constants - ------------------------------------------------------------------------------- --- Types ------------------------------------------------------------------------------- - --- | Heist templates are XML documents. The hexpat library is polymorphic over --- the type of strings, so here we define a 'Node' alias to fix the string --- types of the tag names and tag bodies to 'ByteString'. -type Node = X.Node ByteString ByteString - - ------------------------------------------------------------------------------- --- | A 'Template' is a forest of XML nodes. -type Template = [Node] - - ------------------------------------------------------------------------------- --- | Reversed list of directories -type TPath = [ByteString] - - ------------------------------------------------------------------------------- -type TemplateMap = Map TPath Template - - ------------------------------------------------------------------------------- --- | Holds all the state information needed for template processing: --- --- * a collection of named templates. If you use the @\<apply --- template=\"foo\"\>@ tag to include another template by name, @\"foo\"@ --- is looked up in here. --- --- * the mapping from tag names to 'Splice's. --- --- * a flag to control whether we will recurse during splice processing. --- --- We'll illustrate the recursion flag with a small example template: --- --- > <foo> --- > <bar> --- > ... --- > </bar> --- > </foo> --- --- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ --- splice will result in a list of nodes @L@; if the recursion flag is on we --- will recursively scan @L@ for splices, otherwise @L@ will be included in the --- output verbatim. -data TemplateState m = TemplateState { - -- | A mapping of splice names to splice actions - _spliceMap :: SpliceMap m - -- | A mapping of template names to templates - , _templateMap :: TemplateMap - -- | A flag to control splice recursion - , _recurse :: Bool - , _curContext :: TPath - , _recursionDepth :: Int - , _onLoadHook :: Template -> IO Template - , _preRunHook :: Template -> m Template - , _postRunHook :: Template -> m Template -} - - ------------------------------------------------------------------------------- -instance Eq (TemplateState m) where - a == b = (_recurse a == _recurse b) && - (_templateMap a == _templateMap b) && - (_curContext a == _curContext b) - - ------------------------------------------------------------------------------- --- | 'TemplateMonad' is a monad transformer that gives you access to the 'Node' --- being processed (using the 'MonadReader' instance) as well as holding the --- 'TemplateState' that contains splice and template mappings (accessible --- using the 'MonadState' instance. -newtype TemplateMonad m a = TemplateMonad (RWST Node () (TemplateState m) m a) - deriving ( Monad - , MonadIO - , MonadCatchIO - , MonadReader Node - , MonadState (TemplateState m) ) - - ------------------------------------------------------------------------------- -instance (Monad m) => Monoid (TemplateState m) where - mempty = TemplateState Map.empty Map.empty True [] 0 - return return return - - (TemplateState s1 t1 r1 _ d1 o1 b1 a1) `mappend` - (TemplateState s2 t2 r2 c2 d2 o2 b2 a2) = - TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) - where - s = s1 `mappend` s2 - t = t1 `mappend` t2 - r = r1 && r2 - d = max d1 d2 - - ------------------------------------------------------------------------------- -instance MonadTrans TemplateMonad where - lift = TemplateMonad . lift - ------------------------------------------------------------------------------- --- | A Splice is a TemplateMonad computation that returns [Node]. -type Splice m = TemplateMonad m Template - - ------------------------------------------------------------------------------- --- | SpliceMap associates a name and a Splice. -type SpliceMap m = Map ByteString (Splice m) - - ------------------------------------------------------------------------------- --- TemplateState functions ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- --- | Adds an on-load hook to a `TemplateState`. -addOnLoadHook :: (Monad m) => - (Template -> IO Template) - -> TemplateState m - -> TemplateState m -addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a pre-run hook to a `TemplateState`. -addPreRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a post-run hook to a `TemplateState`. -addPostRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Bind a new splice declaration to a tag name within a 'TemplateState'. -bindSplice :: Monad m => - ByteString -- ^ tag name - -> Splice m -- ^ splice action - -> TemplateState m -- ^ source state - -> TemplateState m -bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a splice. -lookupSplice :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Splice m) -lookupSplice nm ts = Map.lookup nm $ _spliceMap ts - - ------------------------------------------------------------------------------- --- | Converts a path into an array of the elements in reverse order. If the --- path is absolute, we need to remove the leading slash so the split doesn't --- leave @\"\"@ as the last element of the TPath. --- --- FIXME @\"..\"@ currently doesn't work in paths, the solution is non-trivial -splitPaths :: ByteString -> TPath -splitPaths p = if B.null p then [] else (reverse $ B.split '/' path) - where - path = if B.head p == '/' then B.tail p else p - - ------------------------------------------------------------------------------- --- | Does a single template lookup without cascading up. -singleLookup :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm - - ------------------------------------------------------------------------------- --- | Searches for a template by looking in the full path then backing up into each --- of the parent directories until the template is found. -traversePath :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) -traversePath tm path name = - singleLookup tm path name `mplus` - traversePath tm (tail path) name - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a template. -lookupTemplate :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Template, TPath) -lookupTemplate nameStr ts = - f (_templateMap ts) path name - where (name:p) = case splitPaths nameStr of - [] -> [""] - ps -> ps - path = p ++ (_curContext ts) - f = if '/' `B.elem` nameStr - then singleLookup - else traversePath - - ------------------------------------------------------------------------------- --- | Sets the templateMap in a TemplateState. -setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m -setTemplates m ts = ts { _templateMap = m } - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -insertTemplate :: Monad m => - TPath - -> Template - -> TemplateState m - -> TemplateState m -insertTemplate p t st = - setTemplates (Map.insert p t (_templateMap st)) st - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -addTemplate :: Monad m => - ByteString - -> Template - -> TemplateState m - -> TemplateState m -addTemplate n t st = insertTemplate (splitPaths n) t st - - ------------------------------------------------------------------------------- --- | Gets the node currently being processed. -getParamNode :: Monad m => TemplateMonad m Node -getParamNode = ask - - ------------------------------------------------------------------------------- --- | Stops the recursive processing of splices. -stopRecursion :: Monad m => TemplateMonad m () -stopRecursion = modify (\st -> st { _recurse = False }) - - ------------------------------------------------------------------------------- --- | Sets the current context -setContext :: Monad m => TPath -> TemplateMonad m () -setContext c = modify (\st -> st { _curContext = c }) - - ------------------------------------------------------------------------------- --- | Gets the current context -getContext :: Monad m => TemplateMonad m TPath -getContext = gets _curContext - - ------------------------------------------------------------------------------- --- | Performs splice processing on a list of nodes. -runNodeList :: Monad m => [Node] -> Splice m -runNodeList nodes = liftM concat $ sequence (map runNode nodes) - - ------------------------------------------------------------------------------- --- | Performs splice processing on a single node. -runNode :: Monad m => Node -> Splice m -runNode n@(X.Text _) = return [n] -runNode n@(X.Element nm _ ch) = do - s <- liftM (lookupSplice nm) get - maybe runChildren (recurseSplice n) s - - where - runChildren = do - newKids <- runNodeList ch - return [X.modifyChildren (const newKids) n] - - ------------------------------------------------------------------------------- --- | The maximum recursion depth. (Used to prevent infinite loops.) -mAX_RECURSION_DEPTH :: Int -mAX_RECURSION_DEPTH = 20 - - ------------------------------------------------------------------------------- --- | Checks the recursion flag and recurses accordingly. Does not recurse --- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. -recurseSplice :: Monad m => Node -> Splice m -> Splice m -recurseSplice node splice = do - result <- local (const node) splice - ts' <- get - if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH - then do modify (\st -> st { _recursionDepth = _recursionDepth st + 1 }) - res <- runNodeList result - put ts' - return res - else return result - - ------------------------------------------------------------------------------- --- | Runs a splice in the underlying monad. Splices require two --- parameters, the template state, and an input node. -runSplice :: Monad m => - TemplateState m -- ^ The initial template state - -> Node -- ^ The splice's input node - -> Splice m -- ^ The splice - -> m [Node] -runSplice ts node (TemplateMonad splice) = do - (result,_,_) <- runRWST splice node ts - return result - - ------------------------------------------------------------------------------- --- | Runs a template in the underlying monad. Similar to runSplice --- except that templates don't require a Node as a parameter. -runRawTemplate :: Monad m => TemplateState m -> Template -> m [Node] -runRawTemplate ts template = - _preRunHook ts template >>= - runSplice ts (X.Text "") . runNodeList >>= - _postRunHook ts - - ------------------------------------------------------------------------------- --- | Looks up a template name in the supplied 'TemplateState' and runs --- it in the underlying monad. -runTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe [Node]) -runTemplate ts name = - maybe (return Nothing) - (\(t,ctx) -> - return . Just =<< - runRawTemplate (ts {_curContext = ctx}) t) - (lookupTemplate name ts) - - ------------------------------------------------------------------------------- --- | Looks up a template name evaluates it. Same as runTemplate except it --- runs in TemplateMonad instead of m. -evalTemplate :: Monad m - => ByteString - -> TemplateMonad m (Maybe [Node]) -evalTemplate name = do - ts <- get - lift $ runTemplate ts name - - ------------------------------------------------------------------------------- --- | Binds a list of constant string splices -bindStrings :: Monad m - => [(ByteString, ByteString)] - -> TemplateState m - -> TemplateState m -bindStrings pairs ts = foldr add ts pairs - where - add (n,v) = bindSplice n (return [X.Text v]) - - ------------------------------------------------------------------------------- --- | Renders a template with the specified parameters. This is the function --- to use when you want to "call" a template and pass in parameters from code. -callTemplate :: Monad m - => ByteString -- ^ The name of the template - -> [(ByteString, ByteString)] -- ^ Association list of - -- (name,value) parameter pairs - -> TemplateMonad m (Maybe Template) -callTemplate name params = do - modify $ bindStrings params - evalTemplate name - - ------------------------------------------------------------------------------- --- | Renders a template from the specified TemplateState. -renderTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe ByteString) -renderTemplate ts name = do - ns <- runTemplate ts name - return $ (Just . formatList') =<< ns - - ------------------------------------------------------------------------------- -heistExpatOptions :: X.ParserOptions ByteString ByteString -heistExpatOptions = - X.defaultParserOptions { - X.parserEncoding = Just X.UTF8 - , X.entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) - } - ------------------------------------------------------------------------------- --- Template loading ------------------------------------------------------------------------------- - --- | Reads an XML document from disk. -getDoc :: String -> IO (Either String Template) -getDoc f = do - bs <- catch (liftM Right $ B.readFile f) - (\(e::SomeException) -> return $ Left $ show e) - let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" - return $ (mapRight X.getChildren . - mapLeft genErrorMsg . - X.parse' heistExpatOptions . wrap) =<< bs - where - genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str - locMsg (X.XMLParseLocation line col _ _) = - "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" - translate "junk after document element" = "document must have a single root element" - translate s = s - ------------------------------------------------------------------------------- -mapLeft :: (a -> b) -> Either a c -> Either b c -mapLeft g = either (Left . g) Right -mapRight :: (b -> c) -> Either a b -> Either a c -mapRight g = either Left (Right . g) - - ------------------------------------------------------------------------------- --- | Loads a template with the specified path and filename. The --- template is only loaded if it has a ".tpl" extension. -loadTemplate :: String -- ^ path of the template root - -> String -- ^ full file path (includes the template root) - -> IO [Either String (TPath, Template)] --TemplateMap -loadTemplate templateRoot fname - | ".tpl" `isSuffixOf` fname = do - c <- getDoc fname - return [fmap (\t -> (splitPaths $ B.pack tName, t)) c] - | otherwise = return [] - where -- tName is path relative to the template root directory - tName = drop ((length templateRoot)+1) $ - -- We're only dropping the template root, not the whole path - take ((length fname) - 4) fname - - ------------------------------------------------------------------------------- --- | Traverses the specified directory structure and builds a --- TemplateState by loading all the files with a ".tpl" extension. -loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) -loadTemplates dir ts = do - d <- readDirectoryWith (loadTemplate dir) dir - let tlist = F.fold (free d) - errs = lefts tlist - case errs of - [] -> liftM Right $ foldM loadHook ts $ rights tlist - _ -> return $ Left $ unlines errs - - ------------------------------------------------------------------------------- --- | Runs the onLoad hook on the template and returns the `TemplateState` --- with the result inserted. -loadHook :: Monad m => TemplateState m -> (TPath, Template) -> IO (TemplateState m) -loadHook ts (tp, t) = do - t' <- _onLoadHook ts t - return $ insertTemplate tp t' ts - - ------------------------------------------------------------------------------- --- These are here until we can get them into hexpat. ------------------------------------------------------------------------------- - -formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> L.ByteString -formatList nodes = foldl L.append L.empty $ map formatNode nodes - -formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> B.ByteString -formatList' = B.concat . L.toChunks . formatList - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index 83efe53..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Apply where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - ------------------------------------------------------------------------------- --- | Default name for the apply splice. -applyTag :: ByteString -applyTag = "apply" - - ------------------------------------------------------------------------------- --- | Default attribute name for the apply tag. -applyAttr :: ByteString -applyAttr = "template" - - ------------------------------------------------------------------------------- --- | Implementation of the apply splice. -applyImpl :: Monad m => Splice m -applyImpl = do - node <- getParamNode - case X.getAttribute node applyAttr of - Nothing -> return [] -- TODO: error handling - Just attr -> do - st <- get - processedChildren <- runNodeList $ X.getChildren node - modify (bindSplice "content" $ return processedChildren) - maybe (return []) -- TODO: error handling - (\(t,ctx) -> do setContext ctx - result <- runNodeList t - put st - return result) - (lookupTemplate attr (st {_curContext = nextCtx attr st})) - where nextCtx name st - | B.isPrefixOf "/" name = [] - | otherwise = _curContext st - - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Bind.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Bind.html deleted file mode 100644 index 27c602f..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Bind.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Bind where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - --- | Default name for the bind splice. -bindTag :: ByteString -bindTag = "bind" - - ------------------------------------------------------------------------------- --- | Default attribute name for the bind tag. -bindAttr :: ByteString -bindAttr = "tag" - - ------------------------------------------------------------------------------- --- | Implementation of the bind splice. -bindImpl :: Monad m => Splice m -bindImpl = do - node <- getParamNode - maybe (return ()) - (add node) - (X.getAttribute node bindAttr) - return [] - - where - add node nm = modify $ bindSplice nm (return $ X.getChildren node) - - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Ignore.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Ignore.html deleted file mode 100644 index 94cfc79..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Ignore.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Ignore where - ------------------------------------------------------------------------------- -import Data.ByteString.Char8 (ByteString) - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | Default name for the ignore splice. -ignoreTag :: ByteString -ignoreTag = "ignore" - - ------------------------------------------------------------------------------- --- | The ignore tag and everything it surrounds disappears in the --- rendered output. -ignoreImpl :: Monad m => Splice m -ignoreImpl = return [] - - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Markdown.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Markdown.html deleted file mode 100644 index 56cc561..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Markdown.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} - -module Text.Templating.Heist.Splices.Markdown where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as BC -import Data.Maybe -import Control.Concurrent -import Control.Exception (evaluate, throwIO) -import Control.Monad -import Control.Monad.CatchIO -import Control.Monad.Trans -import Data.Typeable -import Prelude hiding (catch) -import System.Directory -import System.Exit -import System.IO -import System.Process -import Text.Templating.Heist.Internal -import Text.XML.Expat.Tree hiding (Node) - - -data PandocMissingException = PandocMissingException - deriving (Typeable) - -instance Show PandocMissingException where - show PandocMissingException = - "Cannot find the \"pandoc\" executable; is it on your $PATH?" - -instance Exception PandocMissingException - - -data MarkdownException = MarkdownException ByteString - deriving (Typeable) - -instance Show MarkdownException where - show (MarkdownException e) = - "Markdown error: pandoc replied:\n\n" ++ BC.unpack e - -instance Exception MarkdownException - - ------------------------------------------------------------------------------- --- | Default name for the markdown splice. -markdownTag :: ByteString -markdownTag = "markdown" - ------------------------------------------------------------------------------- --- | Implementation of the markdown splice. -markdownSplice :: MonadIO m => Splice m -markdownSplice = do - pdMD <- liftIO $ findExecutable "pandoc" - - when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException - - tree <- getParamNode - markup <- liftIO $ - case getAttribute tree "file" of - Just f -> pandoc (fromJust pdMD) $ BC.unpack f - Nothing -> pandocBS (fromJust pdMD) $ textContent tree - - let ee = parse' heistExpatOptions markup - case ee of - (Left e) -> throw $ MarkdownException - $ BC.pack ("Error parsing markdown output: " ++ show e) - (Right n) -> return [n] - - -pandoc :: FilePath -> FilePath -> IO ByteString -pandoc pandocPath inputFile = do - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - - -- FIXME: hardcoded path - args = [ "-S", "--no-wrap", "templates/"++inputFile ] - - -pandocBS :: FilePath -> ByteString -> IO ByteString -pandocBS pandocPath s = do - -- using the crummy string functions for convenience here - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - args = [ "-S", "--no-wrap" ] - - --- a version of readProcessWithExitCode that does I/O properly -readProcessWithExitCode' - :: FilePath -- ^ command to run - -> [String] -- ^ any arguments - -> ByteString -- ^ standard input - -> IO (ExitCode,ByteString,ByteString) -- ^ exitcode, stdout, stderr -readProcessWithExitCode' cmd args input = do - (Just inh, Just outh, Just errh, pid) <- - createProcess (proc cmd args){ std_in = CreatePipe, - std_out = CreatePipe, - std_err = CreatePipe } - outMVar <- newEmptyMVar - - outM <- newEmptyMVar - errM <- newEmptyMVar - - -- fork off a thread to start consuming stdout - forkIO $ do - out <- B.hGetContents outh - putMVar outM out - putMVar outMVar () - - -- fork off a thread to start consuming stderr - forkIO $ do - err <- B.hGetContents errh - putMVar errM err - putMVar outMVar () - - -- now write and flush any input - when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh - hClose inh -- done with stdin - - -- wait on the output - takeMVar outMVar - takeMVar outMVar - hClose outh - - -- wait on the process - ex <- waitForProcess pid - - out <- readMVar outM - err <- readMVar errM - - return (ex, out, err) - - - - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Static.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Static.html deleted file mode 100644 index 734781d..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices-Static.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Static - ( StaticTagState - , bindStaticTag - , clearStaticTagCache - ) where - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Monad -import Control.Monad.Trans -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import Data.IORef -import qualified Data.Map as Map -import Data.Map (Map) -import Data.Maybe -import qualified Data.Set as Set -import System.Random -import Text.XML.Expat.Cursor -import Text.XML.Expat.Tree hiding (Node) - - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | State for storing static tag information -newtype StaticTagState = STS (MVar (Map ByteString Template)) - - ------------------------------------------------------------------------------- --- | Clears the static tag state. -clearStaticTagCache :: StaticTagState -> IO () -clearStaticTagCache (STS staticMVar) = - modifyMVar_ staticMVar (const $ return Map.empty) - - ------------------------------------------------------------------------------- --- | The "static" splice ensures that its contents are evaluated once and then --- cached. The cached contents are returned every time the splice is --- referenced. -staticImpl :: (MonadIO m) - => StaticTagState - -> TemplateMonad m Template -staticImpl (STS mv) = do - tree <- getParamNode - let i = fromJust $ getAttribute tree "id" - - mp <- liftIO $ readMVar mv - - (mp',ns) <- do - let mbn = Map.lookup i mp - case mbn of - Nothing -> do - nodes' <- runNodeList $ getChildren tree - return $! (Map.insert i nodes' mp, nodes') - (Just n) -> do - stopRecursion - return $! (mp,n) - - liftIO $ modifyMVar_ mv (const $ return mp') - - return ns - - ------------------------------------------------------------------------------- --- | Modifies a TemplateState to include a "static" tag. -bindStaticTag :: MonadIO m - => TemplateState m - -> IO (TemplateState m, StaticTagState) -bindStaticTag ts = do - sr <- newIORef $ Set.empty - mv <- liftM STS $ newMVar Map.empty - - return $ (addOnLoadHook (assignIds sr) $ - bindSplice "static" (staticImpl mv) ts, - mv) - - where - generateId :: IO Int - generateId = getStdRandom random - - assignIds setref = mapM f - where - f node = g $ fromTree node - - getId = do - i <- liftM (B.pack . show) generateId - st <- readIORef setref - if Set.member i st - then getId - else do - writeIORef setref $ Set.insert i st - return i - - g curs = do - let node = current curs - curs' <- if getName node == "static" - then do - i <- getId - return $ modifyContent (setAttribute "id" i) curs - else return curs - let mbc = nextDF curs' - maybe (return $ toTree curs') g mbc - - - - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices.html deleted file mode 100644 index 9919a2e..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist-Splices.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
module Text.Templating.Heist.Splices - ( module Text.Templating.Heist.Splices.Apply - , module Text.Templating.Heist.Splices.Bind - , module Text.Templating.Heist.Splices.Ignore - , module Text.Templating.Heist.Splices.Markdown - , module Text.Templating.Heist.Splices.Static - ) where - -import Text.Templating.Heist.Splices.Apply -import Text.Templating.Heist.Splices.Bind -import Text.Templating.Heist.Splices.Ignore -import Text.Templating.Heist.Splices.Markdown -import Text.Templating.Heist.Splices.Static - -- diff --git a/static/docs/0.2.1/heist/src/Text-Templating-Heist.html b/static/docs/0.2.1/heist/src/Text-Templating-Heist.html deleted file mode 100644 index d02f5a7..0000000 --- a/static/docs/0.2.1/heist/src/Text-Templating-Heist.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} - -{-| - - This module contains the core definitions for the Heist template system. - - The Heist template system is based on XML\/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - - The most important concept in Heist is the 'Splice'. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. 'Splice' is implemented as a type synonym @type Splice m = - TemplateMonad m [Node]@, and 'TemplateMonad' has a function 'getParamNode' - that lets you get the input node. - - Suppose you have a place on your page where you want to display a link with - the text \"Logout username\" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - @getUser :: MyAppMonad (Maybe ByteString)@ that gets the current user. - You can implement this functionality with a 'Splice' as follows: - - > - > import Text.XML.Expat.Tree - > - > link :: ByteString -> ByteString -> Node - > link target text = X.Element "a" [("href", target)] [X.Text text] - > - > loginLink :: Node - > loginLink = link "/login" "Login" - > - > logoutLink :: ByteString -> Node - > logoutLink user = link "/logout" (B.append "Logout " user) - > - > loginLogoutSplice :: Splice MyAppMonad - > loginLogoutSplice = do - > user <- lift getUser - > return $ [maybe loginLink logoutLink user] - > - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the 'TemplateState' data structure. The - following code demonstrates how this splice would be used. - - > mySplices = [ ("loginLogout", loginLogoutSplice) ] - > - > main = do - > ets <- loadTemplates "templates" $ - > foldr (uncurry bindSplice) emptyTemplateState mySplices - > let ts = either error id ets - > t <- runMyAppMonad $ renderTemplate ts "index" - > print $ maybe "Page not found" id t - - Here we build up our 'TemplateState' by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final 'TemplateState' wrapped in an Either to handle - errors. Then we use this 'TemplateState' to render our templates. - --} - -module Text.Templating.Heist - ( - -- * Types - Node - , Splice - , Template - , TemplateMonad - , TemplateState - - -- * Functions and declarations on TemplateState values - , addTemplate - , emptyTemplateState - , bindSplice - , lookupSplice - , setTemplates - , loadTemplates - - -- * Hook functions - -- $hookDoc - , addOnLoadHook - , addPreRunHook - , addPostRunHook - - -- * TemplateMonad functions - , stopRecursion - , getParamNode - , runNodeList - , getContext - - -- * Functions for running splices and templates - , runTemplate - , evalTemplate - , callTemplate - , renderTemplate - , bindStrings - - -- * Misc functions - , runSplice - , runRawTemplate - , getDoc - , bindStaticTag - - , heistExpatOptions - , module Text.Templating.Heist.Constants - ) where - -import Control.Monad.Trans -import qualified Data.Map as Map -import Text.Templating.Heist.Internal -import Text.Templating.Heist.Constants -import Text.Templating.Heist.Splices - - ------------------------------------------------------------------------------- --- | The default set of built-in splices. -defaultSpliceMap :: MonadIO m => SpliceMap m -defaultSpliceMap = Map.fromList - [(applyTag, applyImpl) - ,(bindTag, bindImpl) - ,(ignoreTag, ignoreImpl) - ,(markdownTag, markdownSplice) - ] - - ------------------------------------------------------------------------------- --- | An empty template state, with Heist's default splices (@\<bind\>@ and --- @\<apply\>@) mapped. -emptyTemplateState :: MonadIO m => TemplateState m -emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 - return return return - - --- $hookDoc --- Heist hooks allow you to modify templates when they are loaded and before --- and after they are run. Every time you call one of the addAbcHook --- functions the hook is added to onto the processing pipeline. The hooks --- processes the template in the order that they were added to the --- TemplateState. --- --- The pre-run and post-run hooks are run before and after every template is --- run/rendered. You should be careful what code you put in these hooks --- because it can significantly affect the performance of your site. - -- diff --git a/static/docs/0.2.1/heist/src/hscolour.css b/static/docs/0.2.1/heist/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.2.1/heist/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.2.1/snap-core/Data-CIByteString.html b/static/docs/0.2.1/snap-core/Data-CIByteString.html deleted file mode 100644 index cb6f466..0000000 --- a/static/docs/0.2.1/snap-core/Data-CIByteString.html +++ /dev/null @@ -1,366 +0,0 @@ - - -
| |||||||
| |||||||
Description | |||||||
Data.CIByteString is a module containing CIByteString, a wrapper for - ByteString which provides case-insensitive (ASCII-wise) Ord and Eq - instances. - CIByteString also has an IsString instance, so if you use the - "OverloadedStrings" LANGUAGE pragma you can write case-insensitive string - literals, e.g.: - - > let a = "Foo" in - putStrLn $ (show $ unCI a) ++ "==\"FoO\" is " ++ show (a == "FoO") - "Foo"=="FoO" is True - | |||||||
Synopsis | |||||||
| |||||||
Documentation | |||||||
| |||||||
| |||||||
| |||||||
| |||||||
| |||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for (optionally) printing debugging - messages. Normally debug does nothing, but you can pass "-fdebug" to - cabal install to build a snap-core which debugs to stderr. - N.B. this is an internal interface, please don't write external code that - depends on it. - | |||||
Documentation | |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An internal Snap module containing HTTP types. - N.B. this is an internal interface, please don't write user code that - depends on it. Most of these declarations (except for the - unsafe/encapsulation-breaking ones) are re-exported from Snap.Types. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Documentation | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets all of the values for a given header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for debugging iteratees. - N.B. this is an internal interface, please don't write user code that - depends on it. - | |||||
Documentation | |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||
Snap Framework type aliases and utilities for iteratees. Note that as a - convenience, this module also exports everything from Data.Iteratee in the - iteratee library. - WARNING: Note that all of these types are scheduled to change in the - darcs head version of the iteratee library; John Lato et al. are working - on a much improved iteratee formulation. - | |||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Convenience aliases around types from Data.Iteratee - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Re-export types and functions from Data.Iteratee - | |||||||||||||||||||||||||||
module Data.Iteratee | |||||||||||||||||||||||||||
Helper functions - | |||||||||||||||||||||||||||
Enumerators - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a strict bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Enumerates a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Conversion to/from WrappedByteString - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a wrapped bytestring to a lazy bytestring. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Converts a lazy bytestring to a wrapped bytestring. - | |||||||||||||||||||||||||||
Iteratee utilities - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads n elements from a stream and applies the given iteratee to - the stream of the read elements. Reads exactly n elements, and if - the stream is short propagates an error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Reads up to n elements from a stream and applies the given iteratee to the - stream of the read elements. If more than n elements are read, propagates an - error. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Wraps an Iteratee, counting the number of bytes consumed by it. - | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
Buffers an iteratee. - Our enumerators produce a lot of little strings; rather than spending all - our time doing kernel context switches for 4-byte write() calls, we buffer - the iteratee to send 2KB at a time. - | |||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core type definitions, class instances, and functions -for HTTP as well as the Snap monad, which is used for web handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Snap Monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action in the 'Iteratee IO' monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for control flow and early termination - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Short-circuits a Snap monad action early, storing the given - Response value in its state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fails out of a Snap monad action. This is used to indicate - that you choose not to handle the given request within the given - handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Routing - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only if the request's HTTP method matches - the given method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only when rqPathInfo is empty. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A web handler which, given a mapping from URL entry points to web - handlers, efficiently routes requests to the correct handler. - The URL entry points are given as relative paths, for example: - route [ ("foo/bar/quux", fooBarQuux) ] - If the URI of the incoming request is - /foo/bar/quux - or - /foo/bar/quux/...anything... - then the request will be routed to "fooBarQuux", with rqContextPath - set to "/foo/bar/quux/" and rqPathInfo set to - "...anything...". - A path component within an URL entry point beginning with a colon (":") - is treated as a variable capture; the corresponding path component within - the request URI will be entered into the rqParams parameters mapping with - the given name. For instance, if the routes were: - route [ ("foo/:bar/baz", fooBazHandler) ] - Then a request for "/foo/saskatchewan/baz" would be routed to - fooBazHandler with a mapping for: - "bar" => "saskatchewan" - in its parameters table. - Longer paths are matched first, and specific routes are matched before - captures. That is, if given routes: - [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] - a request for "/a/b" will go to h2, "/a/s" for any s will go - to h3, and "/a" will go to h1. - The following example matches "/article" to an article index, - "/login" to a login, and "/article/..." to an article renderer. - route [ ("article", renderIndex) - , ("article/:id", renderArticle) - , ("login", method POST doLogin) ] - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The routeLocal function is the same as route, except it doesn't change - the request's context path. This is useful if you want to route to a - particular handler but you want that handler to receive the rqPathInfo as - it is. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Access to state - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Request object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Response object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Request object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Response object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the Request object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifes the Response object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap action with a locally-modified Request state - object. The Request object in the Snap monad state after the call - to localRequest will be unchanged. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Request from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Response from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logging - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log an error message in the Snap monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabbing request bodies - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sends the request body through an iteratee (data consumer) and - returns the result. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the request body as a bytestring. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Detaches the request body's Enumerator from the Request and - returns it. You would want to use this if you needed to send the - HTTP request body (transformed or otherwise) through to the output - in O(1) space. (Examples: transcoding, "echo", etc) - Normally Snap is careful to ensure that the request body is fully - consumed after your web handler runs; this function is marked - "unsafe" because it breaks this guarantee and leaves the - responsibility up to you. If you don't fully consume the - Enumerator you get here, the next HTTP request in the pipeline - (if any) will misparse. Be careful with exception handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP Datatypes and Functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP-related datatypes: Request, Response, Cookie, etc. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Headers - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Requests - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The server name of the request, as it came in from the request's - Host: header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the port number the HTTP server is listening on. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote IP address. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote TCP port number. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The local IP address for this request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP server's idea of its local hostname. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns True if this is an HTTPS session (currently always - False). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Content-Length of the HTTP request body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP request method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP version used by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns a list of the cookies that came in from the HTTP request - headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Handlers can (will be; --ed) be hung on a URI "entry point"; - this is called the "context path". If a handler is hung on the - context path "/foo/", and you request "/foo/bar", the value - of rqPathInfo will be "bar". - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The "context path" of the request; catenating rqContextPath, and - rqPathInfo should get you back to the original rqURI. The - rqContextPath always begins and ends with a slash ("/") - character, and represents the path (relative to your - component/snaplet) you took to get to your handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the URI requested by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP query string for this Request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Params mapping for this Request. "Parameters" are - automatically decoded from the query string and POST body and - entered into this mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Responses - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status code. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status explanation string. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Response I/O - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the output to be the contents of the specified file. - Calling sendFile will overwrite any output queued to be sent in the - Response. If the response body is not modified after the call to - sendFile, Snap will use the efficient sendfile() system call on - platforms that support it. - If the response body is modified (using modifyResponseBody), the file will - be read using mmap(). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Iteratee - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP utilities - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Description | |||||||||||||
Contains web handlers to serve files from a directory. - | |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
Gets a path from the Request using rqPathInfo and makes sure it is - safe to use for opening files. A path is safe if it is a relative path - and has no .. elements to escape the intended directory structure. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
The default set of mime type mappings we use when serving files. Its - value: - Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - | |||||||||||||
| |||||||||||||
A type alias for MIME type - | |||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||
| ||||||||
Synopsis | ||||||||
| ||||||||
Documentation | ||||||||
| ||||||||
| ||||||||
| ||||||||
| ||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (>) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (A) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (B) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||
Index (C) | ||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (D) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
Index (E) | ||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||
Index (F) | ||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (G) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (H) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (I) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (J) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (L) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (M) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (N) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (O) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||
| ||||||||||||||||||||||||
Index (P) | ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Index (R) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||
Index (S) | ||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (T) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (U) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (W) | |||||||||||||||||||||
|
| |||||||||||||||||||||
Index | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
snap-core-0.2.1: Snap: A Haskell Web Framework (Core) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - This library contains the core definitions and types for the Snap framework, -including: - 1. Primitive types and functions for HTTP (requests, responses, cookies, -post/query parameters, etc) - 2. Type aliases and helper functions for Iteratee I/O - 3. A monad for programming web handlers called "Snap", inspired by -happstack's (http://happstack.com/index.html), which allows: -
Quick start: The Snap monad and HTTP definitions are in Snap.Types, -some iteratee utilities are in Snap.Iteratee. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - --- | "Data.CIByteString" is a module containing 'CIByteString', a wrapper for --- 'ByteString' which provides case-insensitive (ASCII-wise) 'Ord' and 'Eq' --- instances. --- --- 'CIByteString' also has an 'IsString' instance, so if you use the --- \"OverloadedStrings\" LANGUAGE pragma you can write case-insensitive string --- literals, e.g.: --- --- @ --- \> let a = \"Foo\" in --- putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++ show (a == \"FoO\") --- \"Foo\"==\"FoO\" is True --- @ - -module Data.CIByteString - ( CIByteString - , toCI - , unCI - , ciToLower - ) where - --- for IsString instance -import Data.ByteString.Char8 () -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString as S -import Data.Char -import Data.String - - --- | A case-insensitive newtype wrapper for 'ByteString' -data CIByteString = CIByteString { unCI :: !ByteString - , _lowercased :: !ByteString } - -toCI :: ByteString -> CIByteString -toCI s = CIByteString s t - where - t = lowercase s - -ciToLower :: CIByteString -> ByteString -ciToLower = _lowercased - -instance Show CIByteString where - show (CIByteString s _) = show s - -lowercase :: ByteString -> ByteString -lowercase = S.map (c2w . toLower . w2c) - -instance Eq CIByteString where - (CIByteString _ a) == (CIByteString _ b) = a == b - (CIByteString _ a) /= (CIByteString _ b) = a /= b - -instance Ord CIByteString where - (CIByteString _ a) <= (CIByteString _ b) = a <= b - -instance IsString CIByteString where - fromString = toCI . fromString -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Internal-Debug.html b/static/docs/0.2.1/snap-core/src/Snap-Internal-Debug.html deleted file mode 100644 index c295589..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Internal-Debug.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - -
-- | An internal Snap module for (optionally) printing debugging --- messages. Normally 'debug' does nothing, but you can pass \"-fdebug\" to --- @cabal install@ to build a @snap-core@ which debugs to stderr. --- --- /N.B./ this is an internal interface, please don't write external code that --- depends on it. - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} - -module Snap.Internal.Debug where - -import Control.Monad.Trans - -#ifdef DEBUG_TEST - -debug :: (MonadIO m) => String -> m () -debug !s = return $ s `seq` () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno !s = return $ s `seq` () - -#elif defined(DEBUG) - ------------------------------------------------------------------------------- -import Control.Concurrent -import Data.List -import Data.Maybe -import Foreign.C.Error -import System.IO -import System.IO.Unsafe -import Text.Printf ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -_debugMVar :: MVar () -_debugMVar = unsafePerformIO $ newMVar () - - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug s = liftIO $ withMVar _debugMVar $ \_ -> do - tid <- myThreadId - hPutStrLn stderr $ s' tid - hFlush stderr - where - chop x = let y = fromMaybe x $ stripPrefix "ThreadId " x - in printf "%8s" y - - s' t = "[" ++ chop (show t) ++ "] " ++ s - -{-# INLINE debug #-} - - ------------------------------------------------------------------------------- -debugErrno :: (MonadIO m) => String -> m () -debugErrno loc = liftIO $ do - err <- getErrno - let ex = errnoToIOError loc err Nothing Nothing - debug $ show ex ------------------------------------------------------------------------------- - -#else - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug _ = return () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno _ = return () ------------------------------------------------------------------------------- - -#endif -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Internal-Http-Types.html b/static/docs/0.2.1/snap-core/src/Snap-Internal-Http-Types.html deleted file mode 100644 index 44d3120..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Internal-Http-Types.html +++ /dev/null @@ -1,648 +0,0 @@ - - - - -
-- | An internal Snap module containing HTTP types. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. Most of these declarations (except for the --- unsafe/encapsulation-breaking ones) are re-exported from "Snap.Types". - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeSynonymInstances #-} - -module Snap.Internal.Http.Types where - - ------------------------------------------------------------------------------- -import Control.Applicative hiding (empty) -import Control.Monad (liftM, when) -import qualified Data.Attoparsec as Atto -import Data.Attoparsec hiding (many, Result(..)) -import Data.Bits -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w,w2c) -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import qualified Data.ByteString as S -import Data.Char -import Data.DList (DList) -import qualified Data.DList as DL -import Data.IORef -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Monoid -import Data.Serialize.Builder -import Data.Time.Clock -import Data.Time.Format -import Data.Word -import Foreign hiding (new) -import Foreign.C.String -import Foreign.C.Types -import Prelude hiding (take) -import System.Locale (defaultTimeLocale) - ------------------------------------------------------------------------------- -import Data.CIByteString -import qualified Snap.Iteratee as I - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "set_c_locale" - set_c_locale :: IO () - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_parse_http_time" - c_parse_http_time :: CString -> IO CTime - - ------------------------------------------------------------------------------- -foreign import ccall unsafe "c_format_http_time" - c_format_http_time :: CTime -> CString -> IO () - ------------------------------------------------------------------------------- -type Enumerator a = I.Enumerator IO a - ------------------------------------------------------------------------------- --- | A type alias for a case-insensitive key-value mapping. -type Headers = Map CIByteString [ByteString] - - ------------------------------------------------------------------------------- --- | A typeclass for datatypes which contain HTTP headers. -class HasHeaders a where - - -- | Modify the datatype's headers. - updateHeaders :: (Headers -> Headers) -> a -> a - - -- | Retrieve the headers from a datatype that has headers. - headers :: a -> Headers - - ------------------------------------------------------------------------------- --- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header with --- the same name already exists, the new value is appended to the headers list. -addHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -addHeader k v = updateHeaders $ Map.insertWith' (++) k [v] - - ------------------------------------------------------------------------------- --- | Sets a header key-value-pair in a 'HasHeaders' datatype. If a header with --- the same name already exists, it is overwritten with the new value. -setHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -setHeader k v = updateHeaders $ Map.insert k [v] - - ------------------------------------------------------------------------------- --- | Gets all of the values for a given header. -getHeaders :: (HasHeaders a) => CIByteString -> a -> Maybe [ByteString] -getHeaders k a = Map.lookup k $ headers a - - ------------------------------------------------------------------------------- --- | Gets a header value out of a 'HasHeaders' datatype. If many headers came --- in with the same name, they will be catenated together. -getHeader :: (HasHeaders a) => CIByteString -> a -> Maybe ByteString -getHeader k a = liftM (S.intercalate " ") (Map.lookup k $ headers a) - - ------------------------------------------------------------------------------- --- | Enumerates the HTTP method values (see --- <http://tools.ietf.org/html/rfc2068.html#section-5.1.1>). -data Method = GET | HEAD | POST | PUT | DELETE | TRACE | OPTIONS | CONNECT - deriving(Show,Read,Ord,Eq) - - ------------------------------------------------------------------------------- -type HttpVersion = (Int,Int) - - ------------------------------------------------------------------------------- --- | A datatype representing an HTTP cookie. -data Cookie = Cookie { - -- | The name of the cookie. - cookieName :: !ByteString - - -- | The cookie's string value. - , cookieValue :: !ByteString - - -- | The cookie's expiration value, if it has one. - , cookieExpires :: !(Maybe UTCTime) - - -- | The cookie's \"domain\" value, if it has one. - , cookieDomain :: !(Maybe ByteString) - - -- | The cookie path. - , cookiePath :: !(Maybe ByteString) -} deriving (Eq, Show) - - ------------------------------------------------------------------------------- --- | A type alias for the HTTP parameters mapping. Each parameter --- key maps to a list of ByteString values; if a parameter is specified --- multiple times (e.g.: \"@GET /foo?param=bar1¶m=bar2@\"), looking up --- \"@param@\" in the mapping will give you @[\"bar1\", \"bar2\"]@. -type Params = Map ByteString [ByteString] - - ------------------------------------------------------------------------------- --- request type ------------------------------------------------------------------------------- - -data SomeEnumerator = SomeEnumerator (forall a . Enumerator a) - - ------------------------------------------------------------------------------- --- | Contains all of the information about an incoming HTTP request. -data Request = Request - { -- | The server name of the request, as it came in from the request's - -- @Host:@ header. - rqServerName :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqServerPort :: !Int - - -- | The remote IP address. - , rqRemoteAddr :: !ByteString - - -- | The remote TCP port number. - , rqRemotePort :: !Int - - -- | The local IP address for this request. - , rqLocalAddr :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqLocalPort :: !Int - - -- | Returns the HTTP server's idea of its local hostname. - , rqLocalHostname :: !ByteString - - -- | Returns @True@ if this is an @HTTPS@ session (currently always - -- @False@). - , rqIsSecure :: !Bool - , rqHeaders :: Headers - , rqBody :: IORef SomeEnumerator - - -- | Returns the @Content-Length@ of the HTTP request body. - , rqContentLength :: !(Maybe Int) - - -- | Returns the HTTP request method. - , rqMethod :: !Method - - -- | Returns the HTTP version used by the client. - , rqVersion :: !HttpVersion - - -- | Returns a list of the cookies that came in from the HTTP request - -- headers. - , rqCookies :: [Cookie] - - - -- | We'll be doing web components (or \"snaplets\") for version 0.2. The - -- \"snaplet path\" refers to the place on the URL where your containing - -- snaplet is hung. The value of 'rqSnapletPath' is either @\"\"@ (at the - -- top-level context) or is a path beginning with a slash, but not ending - -- with one. - -- - -- An identity is that: - -- - -- > rqURI r == 'S.concat' [ rqSnapletPath r - -- > , rqContextPath r - -- > , rqPathInfo r ] - -- - -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will be - -- \"\" - , rqSnapletPath :: !ByteString - - -- | Handlers can (/will be; --ed/) be hung on a @URI@ \"entry point\"; - -- this is called the \"context path\". If a handler is hung on the - -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the value - -- of 'rqPathInfo' will be @\"bar\"@. - , rqPathInfo :: !ByteString - - -- | The \"context path\" of the request; catenating 'rqContextPath', and - -- 'rqPathInfo' should get you back to the original 'rqURI'. The - -- 'rqContextPath' always begins and ends with a slash (@\"\/\"@) - -- character, and represents the path (relative to your - -- component\/snaplet) you took to get to your handler. - , rqContextPath :: !ByteString - - -- | Returns the @URI@ requested by the client. - , rqURI :: !ByteString - - -- | Returns the HTTP query string for this 'Request'. - , rqQueryString :: !ByteString - - -- | Returns the 'Params' mapping for this 'Request'. \"Parameters\" are - -- automatically decoded from the query string and @POST@ body and - -- entered into this mapping. - , rqParams :: Params - } - - ------------------------------------------------------------------------------- -instance Show Request where - show r = concat [ "Request <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - sname - , remote - , local - , beginheaders - , hdrs - , endheaders - , contentlength - , method - , version - , cookies - , pathinfo - , contextpath - , snapletpath - , uri - , params - ] - - sname = concat [ "server-name: ", toStr $ rqServerName r ] - remote = concat [ "remote: " - , toStr $ rqRemoteAddr r - , ":" - , show (rqRemotePort r) - ] - local = concat [ "local: " - , toStr $ rqLocalAddr r - , ":" - , show $ rqServerPort r - ] - beginheaders = "Headers:\n ========================================" - endheaders = " ========================================" - hdrs = " " ++ show (rqHeaders r) - contentlength = concat [ "content-length: " - , show $ rqContentLength r - ] - method = concat [ "method: " - , show $ rqMethod r - ] - version = concat [ "version: " - , show $ rqVersion r - ] - cookies = concat [ "cookies:\n" - , " ========================================\n" - , " " ++ (show $ rqCookies r) - , "\n ========================================" - ] - pathinfo = concat [ "pathinfo: ", toStr $ rqPathInfo r ] - contextpath = concat [ "contextpath: ", toStr $ rqContextPath r ] - snapletpath = concat [ "snapletpath: ", toStr $ rqSnapletPath r ] - uri = concat [ "URI: ", toStr $ rqURI r ] - params = concat [ "params:\n" - , " ========================================\n" - , " " ++ (show $ rqParams r) - , "\n ========================================" - ] - - ------------------------------------------------------------------------------- -instance HasHeaders Request where - headers = rqHeaders - updateHeaders f r = r { rqHeaders = f (rqHeaders r) } - - ------------------------------------------------------------------------------- -instance HasHeaders Headers where - headers = id - updateHeaders = id - ------------------------------------------------------------------------------- --- response type ------------------------------------------------------------------------------- - -data ResponseBody = Enum (forall a . Enumerator a) -- ^ output body is enumerator - | SendFile FilePath -- ^ output body is sendfile() - - ------------------------------------------------------------------------------- -rspBodyMap :: (forall a . Enumerator a -> Enumerator a) - -> ResponseBody - -> ResponseBody -rspBodyMap f b = Enum $ f $ rspBodyToEnum b - - ------------------------------------------------------------------------------- -rspBodyToEnum :: ResponseBody -> Enumerator a -rspBodyToEnum (Enum e) = e -rspBodyToEnum (SendFile fp) = I.enumFile fp - - ------------------------------------------------------------------------------- --- | Represents an HTTP response. -data Response = Response - { rspHeaders :: Headers - , rspHttpVersion :: !HttpVersion - - -- | We will need to inspect the content length no matter what, and - -- looking up \"content-length\" in the headers and parsing the number - -- out of the text will be too expensive. - , rspContentLength :: !(Maybe Int) - , rspBody :: ResponseBody - - -- | Returns the HTTP status code. - , rspStatus :: !Int - - -- | Returns the HTTP status explanation string. - , rspStatusReason :: !ByteString - } - - ------------------------------------------------------------------------------- -instance Show Response where - show r = concat [ "Response <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - hdrs - , version - , status - , reason - ] - - hdrs = concat [ "headers:\n" - , " ==============================\n " - , show $ rspHeaders r - , "\n ==============================" ] - - version = concat [ "version: ", show $ rspHttpVersion r ] - status = concat [ "status: ", show $ rspStatus r ] - reason = concat [ "reason: ", toStr $ rspStatusReason r ] - - ------------------------------------------------------------------------------- -instance HasHeaders Response where - headers = rspHeaders - updateHeaders f r = r { rspHeaders = f (rspHeaders r) } - - ------------------------------------------------------------------------------- --- | Looks up the value(s) for the given named parameter. Parameters initially --- come from the request's query string and any decoded POST body (if the --- request's @Content-Type@ is @application\/x-www-form-urlencoded@). Parameter --- values can be modified within handlers using "rqModifyParams". -rqParam :: ByteString -- ^ parameter name to look up - -> Request -- ^ HTTP request - -> Maybe [ByteString] -rqParam k rq = Map.lookup k $ rqParams rq -{-# INLINE rqParam #-} - - ------------------------------------------------------------------------------- --- | Modifies the parameters mapping (which is a @Map ByteString ByteString@) in --- a 'Request' using the given function. -rqModifyParams :: (Params -> Params) -> Request -> Request -rqModifyParams f r = r { rqParams = p } - where - p = f $ rqParams r -{-# INLINE rqModifyParams #-} - - ------------------------------------------------------------------------------- --- | Writes a key-value pair to the parameters mapping within the given request. -rqSetParam :: ByteString -- ^ parameter name - -> [ByteString] -- ^ parameter values - -> Request -- ^ request - -> Request -rqSetParam k v = rqModifyParams $ Map.insert k v -{-# INLINE rqSetParam #-} - ------------------------------------------------------------------------------- --- responses ------------------------------------------------------------------------------- - --- | An empty 'Response'. -emptyResponse :: Response -emptyResponse = Response Map.empty (1,1) Nothing (Enum return) 200 "OK" - - ------------------------------------------------------------------------------- --- | Sets an HTTP response body to the given 'Enumerator' value. -setResponseBody :: (forall a . Enumerator a) -- ^ new response body - -- enumerator - -> Response -- ^ response to modify - -> Response -setResponseBody e r = r { rspBody = Enum e } -{-# INLINE setResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the HTTP response status. -setResponseStatus :: Int -- ^ HTTP response integer code - -> ByteString -- ^ HTTP response explanation - -> Response -- ^ Response to be modified - -> Response -setResponseStatus s reason r = r { rspStatus=s, rspStatusReason=reason } -{-# INLINE setResponseStatus #-} - - ------------------------------------------------------------------------------- --- | Modifies a response body. -modifyResponseBody :: (forall a . Enumerator a -> Enumerator a) - -> Response - -> Response -modifyResponseBody f r = r { rspBody = rspBodyMap f (rspBody r) } -{-# INLINE modifyResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the @Content-Type@ in the 'Response' headers. -setContentType :: ByteString -> Response -> Response -setContentType = setHeader "Content-Type" -{-# INLINE setContentType #-} - - ------------------------------------------------------------------------------- --- | Adds an HTTP 'Cookie' to the 'Response' headers. -addCookie :: Cookie -- ^ cookie value - -> Response -- ^ response to modify - -> Response -addCookie (Cookie k v mbExpTime mbDomain mbPath) = updateHeaders f - where - f = Map.insertWith' (++) "Set-Cookie" [cookie] - cookie = S.concat [k, "=", v, path, exptime, domain] - path = maybe "" (S.append "; path=") mbPath - domain = maybe "" (S.append "; domain=") mbDomain - exptime = maybe "" (S.append "; expires=" . fmt) mbExpTime - fmt = fromStr . formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT" - - ------------------------------------------------------------------------------- --- | A note here: if you want to set the @Content-Length@ for the response, --- Snap forces you to do it with this function rather than by setting it in the --- headers; the @Content-Length@ in the headers will be ignored. --- --- The reason for this is that Snap needs to look up the value of --- @Content-Length@ for each request, and looking the string value up in the --- headers and parsing the number out of the text will be too expensive. --- --- If you don't set a content length in your response, HTTP keep-alive will be --- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For HTTP\/1.1 --- clients, Snap will switch to the chunked transfer encoding if --- @Content-Length@ is not specified. -setContentLength :: Int -> Response -> Response -setContentLength l r = r { rspContentLength = Just l } -{-# INLINE setContentLength #-} - - ------------------------------------------------------------------------------- --- | Removes any @Content-Length@ set in the 'Response'. -clearContentLength :: Response -> Response -clearContentLength r = r { rspContentLength = Nothing } -{-# INLINE clearContentLength #-} - - ------------------------------------------------------------------------------- --- HTTP dates - -{- --- | Converts a 'ClockTime' into an HTTP timestamp. -formatHttpTime :: UTCTime -> ByteString -formatHttpTime = fromStr . formatTime defaultTimeLocale "%a, %d %b %Y %X GMT" - --- | Converts an HTTP timestamp into a 'UTCTime'. -parseHttpTime :: ByteString -> Maybe UTCTime -parseHttpTime s' = - parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" s - where - s = toStr s' --} - --- | Converts a 'CTime' into an HTTP timestamp. -formatHttpTime :: CTime -> IO ByteString -formatHttpTime t = allocaBytes 40 $ \ptr -> do - c_format_http_time t ptr - S.packCString ptr - - ------------------------------------------------------------------------------- --- | Converts an HTTP timestamp into a 'CTime'. -parseHttpTime :: ByteString -> IO CTime -parseHttpTime s = S.useAsCString s $ \ptr -> - c_parse_http_time ptr - - ------------------------------------------------------------------------------- --- URL ENCODING ------------------------------------------------------------------------------- - -parseToCompletion :: Parser a -> ByteString -> Maybe a -parseToCompletion p s = toResult $ finish r - where - r = parse p s - - toResult (Atto.Done _ c) = Just c - toResult _ = Nothing - - ------------------------------------------------------------------------------- -pUrlEscaped :: Parser ByteString -pUrlEscaped = do - sq <- nextChunk DL.empty - return $ S.concat $ DL.toList sq - - where - nextChunk :: DList ByteString -> Parser (DList ByteString) - nextChunk s = (endOfInput *> pure s) <|> do - c <- anyWord8 - case w2c c of - '+' -> plusSpace s - '%' -> percentEncoded s - _ -> unEncoded c s - - percentEncoded :: DList ByteString -> Parser (DList ByteString) - percentEncoded l = do - hx <- take 2 - when (S.length hx /= 2 || - (not $ S.all (isHexDigit . w2c) hx)) $ - fail "bad hex in url" - - let code = (Cvt.hex hx) :: Word8 - nextChunk $ DL.snoc l (S.singleton code) - - unEncoded :: Word8 -> DList ByteString -> Parser (DList ByteString) - unEncoded c l' = do - let l = DL.snoc l' (S.singleton c) - bs <- takeTill (flip elem (map c2w "%+")) - if S.null bs - then nextChunk l - else nextChunk $ DL.snoc l bs - - plusSpace :: DList ByteString -> Parser (DList ByteString) - plusSpace l = nextChunk (DL.snoc l (S.singleton $ c2w ' ')) - - ------------------------------------------------------------------------------- --- | Decodes an URL-escaped string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlDecode :: ByteString -> Maybe ByteString -urlDecode = parseToCompletion pUrlEscaped - - ------------------------------------------------------------------------------- --- "...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," --- [not including the quotes - ed], and reserved characters used for their --- reserved purposes may be used unencoded within a URL." - --- | URL-escapes a string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlEncode :: ByteString -> ByteString -urlEncode = toByteString . S.foldl' f empty - where - f b c = - if c == c2w ' ' - then b `mappend` singleton (c2w '+') - else if isKosher c - then b `mappend` singleton c - else b `mappend` hexd c - - isKosher w = any ($ c) [ isAlphaNum - , flip elem ['$', '-', '.', '!', '*' - , '\'', '(', ')', ',' ]] - where - c = w2c w - - ------------------------------------------------------------------------------- -hexd :: Word8 -> Builder -hexd c = singleton (c2w '%') `mappend` singleton hi `mappend` singleton low - where - d = c2w . intToDigit - low = d $ fromEnum $ c .&. 0xf - hi = d $ fromEnum $ (c .&. 0xf0) `shift` (-4) - - ------------------------------------------------------------------------------- -finish :: Atto.Result a -> Atto.Result a -finish (Atto.Partial f) = flip feed "" $ f "" -finish x = x - - ------------------------------------------------------------------------------- --- local definitions -fromStr :: String -> ByteString -fromStr = S.pack . map c2w -{-# INLINE fromStr #-} - ------------------------------------------------------------------------------- --- private helper functions -toStr :: ByteString -> String -toStr = map w2c . S.unpack - -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Internal-Iteratee-Debug.html b/static/docs/0.2.1/snap-core/src/Snap-Internal-Iteratee-Debug.html deleted file mode 100644 index 972fc9d..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Internal-Iteratee-Debug.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
-- | An internal Snap module for debugging iteratees. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. - -{-# OPTIONS_GHC -fno-warn-orphans #-} -{-# LANGUAGE FlexibleInstances #-} - -module Snap.Internal.Iteratee.Debug ( debugIteratee ) where - ------------------------------------------------------------------------------- -import Data.Iteratee.WrappedByteString -import Data.Word (Word8) -import System.IO ------------------------------------------------------------------------------- -import Snap.Iteratee ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -instance Show (WrappedByteString Word8) where - show (WrapBS s) = show s - - ------------------------------------------------------------------------------- -debugIteratee :: Iteratee IO () -debugIteratee = IterateeG f - where - f c@(EOF _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return (Done () c) - - f c@(Chunk _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return $ Cont debugIteratee Nothing -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Internal-Routing.html b/static/docs/0.2.1/snap-core/src/Snap-Internal-Routing.html deleted file mode 100644 index 63cffe2..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Internal-Routing.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - -
module Snap.Internal.Routing where - - ------------------------------------------------------------------------------- -import Control.Applicative ((<|>)) -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.Monoid -import qualified Data.Map as Map - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Types - - ------------------------------------------------------------------------------- -{-| - -The internal data type you use to build a routing tree. Matching is -done unambiguously. - -'Capture' and 'Dir' routes can have a "fallback" route: - - - For 'Capture', the fallback is routed when there is nothing to capture - - For 'Dir', the fallback is routed when we can't find a route in its map - -Fallback routes are stacked: i.e. for a route like: - -> Dir [("foo", Capture "bar" (Action bar) NoRoute)] baz - -visiting the URI foo/ will result in the "bar" capture being empty and -triggering its fallback. It's NoRoute, so we go to the nearest parent -fallback and try that, which is the baz action. - --} -data Route a = Action (Snap a) -- wraps a 'Snap' action - | Capture ByteString (Route a) (Route a) -- captures the dir in a param - | Dir (Map.Map ByteString (Route a)) (Route a) -- match on a dir - | NoRoute - - ------------------------------------------------------------------------------- -instance Monoid (Route a) where - mempty = NoRoute - - -- Unions two routes, favoring the right-hand side - mappend NoRoute r = r - - mappend l@(Action _) r = case r of - (Action _) -> r - (Capture p r' fb) -> Capture p r' (mappend fb l) - (Dir _ _) -> mappend (Dir Map.empty l) r - NoRoute -> l - - mappend l@(Capture p r' fb) r = case r of - (Action _) -> Capture p r' (mappend fb r) - (Capture p' r'' fb') - | p == p' -> Capture p (mappend r' r'') (mappend fb fb') - | otherwise -> r - (Dir rm fb') -> Dir rm (mappend fb' l) - NoRoute -> l - - mappend l@(Dir rm fb) r = case r of - (Action _) -> Dir rm (mappend fb r) - (Capture _ _ _) -> Dir rm (mappend fb r) - (Dir rm' fb') -> Dir (Map.unionWith mappend rm rm') (mappend fb fb') - NoRoute -> l - - ------------------------------------------------------------------------------- --- | A web handler which, given a mapping from URL entry points to web --- handlers, efficiently routes requests to the correct handler. --- --- The URL entry points are given as relative paths, for example: --- --- > route [ ("foo/bar/quux", fooBarQuux) ] --- --- If the URI of the incoming request is --- --- > /foo/bar/quux --- --- or --- --- > /foo/bar/quux/...anything... --- --- then the request will be routed to \"@fooBarQuux@\", with 'rqContextPath' --- set to \"@\/foo\/bar\/quux\/@\" and 'rqPathInfo' set to --- \"@...anything...@\". --- --- A path component within an URL entry point beginning with a colon (\"@:@\") --- is treated as a /variable capture/; the corresponding path component within --- the request URI will be entered into the 'rqParams' parameters mapping with --- the given name. For instance, if the routes were: --- --- > route [ ("foo/:bar/baz", fooBazHandler) ] --- --- Then a request for \"@\/foo\/saskatchewan\/baz@\" would be routed to --- @fooBazHandler@ with a mapping for: --- --- > "bar" => "saskatchewan" --- --- in its parameters table. --- --- Longer paths are matched first, and specific routes are matched before --- captures. That is, if given routes: --- --- > [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] --- --- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will go --- to @h3@, and \"@\/a@\" will go to @h1@. --- --- The following example matches \"@\/article@\" to an article index, --- \"@\/login@\" to a login, and \"@\/article\/...@\" to an article renderer. --- --- > route [ ("article", renderIndex) --- > , ("article/:id", renderArticle) --- > , ("login", method POST doLogin) ] --- -route :: [(ByteString, Snap a)] -> Snap a -route rts = route' (return ()) rts' [] - where - rts' = mconcat (map pRoute rts) - - ------------------------------------------------------------------------------- --- | The 'routeLocal' function is the same as 'route', except it doesn't change --- the request's context path. This is useful if you want to route to a --- particular handler but you want that handler to receive the 'rqPathInfo' as --- it is. -routeLocal :: [(ByteString, Snap a)] -> Snap a -routeLocal rts' = do - req <- getRequest - let ctx = rqContextPath req - let p = rqPathInfo req - let md = modifyRequest $ \r -> r {rqContextPath=ctx, rqPathInfo=p} - - route' md rts [] <|> (md >> pass) - - where - rts = mconcat (map pRoute rts') - - ------------------------------------------------------------------------------- -pRoute :: (ByteString, Snap a) -> Route a -pRoute (r, a) = foldr f (Action a) hier - where - hier = filter (not . B.null) $ B.splitWith (== (c2w '/')) r - f s rt = if B.head s == c2w ':' - then Capture (B.tail s) rt NoRoute - else Dir (Map.fromList [(s, rt)]) NoRoute - - ------------------------------------------------------------------------------- -route' :: Snap () -- ^ an action to be run before any user - -- handler - -> Route a -- ^ currently active routing table - -> [Route a] -- ^ list of fallback routing tables in case - -- the current table fails - -> Snap a -route' pre (Action action) _ = pre >> action - -route' pre (Capture param rt fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - if B.null cwd - then route' pre fb fbs - else do localRequest (updateContextPath (B.length cwd) . (f cwd)) $ - route' pre rt (fb:fbs) - where - f v req = req { rqParams = Map.insertWith (++) param [v] (rqParams req) } - -route' pre (Dir rtm fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - case Map.lookup cwd rtm of - Just rt -> do - localRequest (updateContextPath (B.length cwd)) $ - route' pre rt (fb:fbs) - Nothing -> route' pre fb fbs - -route' _ NoRoute [] = pass -route' pre NoRoute (fb:fbs) = route' pre fb fbs -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Internal-Types.html b/static/docs/0.2.1/snap-core/src/Snap-Internal-Types.html deleted file mode 100644 index 1ec95c1..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Internal-Types.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Types where - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Exception (throwIO, ErrorCall(..)) -import Control.Monad.CatchIO -import Control.Monad.State.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.IORef -import qualified Data.Iteratee as Iter -import Data.Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Data.Text.Lazy as LT -import qualified Data.Text.Lazy.Encoding as LT - -import Data.Typeable - ------------------------------------------------------------------------------- -import Snap.Iteratee hiding (Enumerator) -import Snap.Internal.Http.Types - - ------------------------------------------------------------------------------- --- The Snap Monad ------------------------------------------------------------------------------- - -{-| - -'Snap' is the 'Monad' that user web handlers run in. 'Snap' gives you: - -1. stateful access to fetch or modify an HTTP 'Request' - -2. stateful access to fetch or modify an HTTP 'Response' - -3. failure \/ 'Alternative' \/ 'MonadPlus' semantics: a 'Snap' handler can - choose not to handle a given request, using 'empty' or its synonym 'pass', - and you can try alternative handlers with the '<|>' operator: - - > a :: Snap String - > a = pass - > - > b :: Snap String - > b = return "foo" - > - > c :: Snap String - > c = a <|> b -- try running a, if it fails then try b - -4. convenience functions ('writeBS', 'writeLBS', 'writeText', 'writeLazyText', - 'addToOutput') for writing output to the 'Response': - - > a :: (forall a . Enumerator a) -> Snap () - > a someEnumerator = do - > writeBS "I'm a strict bytestring" - > writeLBS "I'm a lazy bytestring" - > addToOutput someEnumerator - -5. early termination: if you call 'finishWith': - - > a :: Snap () - > a = do - > modifyResponse $ setResponseStatus 500 "Internal Server Error" - > writeBS "500 error" - > r <- getResponse - > finishWith r - - then any subsequent processing will be skipped and supplied 'Response' value - will be returned from 'runSnap' as-is. - -6. access to the 'IO' monad through a 'MonadIO' instance: - - > a :: Snap () - > a = liftIO fireTheMissiles --} - - ------------------------------------------------------------------------------- -newtype Snap a = Snap { - unSnap :: StateT SnapState (Iteratee IO) (Maybe (Either Response a)) -} - - ------------------------------------------------------------------------------- -data SnapState = SnapState - { _snapRequest :: Request - , _snapResponse :: Response - , _snapLogError :: ByteString -> IO () } - - ------------------------------------------------------------------------------- -instance Monad Snap where - (Snap m) >>= f = - Snap $ do - eth <- m - maybe (return Nothing) - (either (return . Just . Left) - (unSnap . f)) - eth - - return = Snap . return . Just . Right - fail = const $ Snap $ return Nothing - - ------------------------------------------------------------------------------- -instance MonadIO Snap where - liftIO m = Snap $ liftM (Just . Right) $ liftIO m - - ------------------------------------------------------------------------------- -instance MonadCatchIO Snap where - catch (Snap m) handler = Snap $ do - x <- try m - case x of - (Left e) -> let (Snap z) = handler e in z - (Right y) -> return y - - block (Snap m) = Snap $ block m - unblock (Snap m) = Snap $ unblock m - - ------------------------------------------------------------------------------- -instance MonadPlus Snap where - mzero = Snap $ return Nothing - - a `mplus` b = - Snap $ do - mb <- unSnap a - if isJust mb then return mb else unSnap b - - ------------------------------------------------------------------------------- -instance Functor Snap where - fmap = liftM - - ------------------------------------------------------------------------------- -instance Applicative Snap where - pure = return - (<*>) = ap - - ------------------------------------------------------------------------------- -instance Alternative Snap where - empty = mzero - (<|>) = mplus - - ------------------------------------------------------------------------------- -liftIter :: Iteratee IO a -> Snap a -liftIter i = Snap (lift i >>= return . Just . Right) - - ------------------------------------------------------------------------------- --- | Sends the request body through an iteratee (data consumer) and --- returns the result. -runRequestBody :: Iteratee IO a -> Snap a -runRequestBody iter = do - req <- getRequest - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - - -- make sure the iteratee consumes all of the output - let iter' = iter >>= (\a -> Iter.skipToEof >> return a) - - -- run the iteratee - result <- liftIter $ Iter.joinIM $ enum iter' - - -- stuff a new dummy enumerator into the request, so you can only try to - -- read the request body from the socket once - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . Iter.joinI . Iter.take 0 ) - - return result - - ------------------------------------------------------------------------------- --- | Returns the request body as a bytestring. -getRequestBody :: Snap L.ByteString -getRequestBody = liftM fromWrap $ runRequestBody stream2stream -{-# INLINE getRequestBody #-} - - ------------------------------------------------------------------------------- --- | Detaches the request body's 'Enumerator' from the 'Request' and --- returns it. You would want to use this if you needed to send the --- HTTP request body (transformed or otherwise) through to the output --- in O(1) space. (Examples: transcoding, \"echo\", etc) --- --- Normally Snap is careful to ensure that the request body is fully --- consumed after your web handler runs; this function is marked --- \"unsafe\" because it breaks this guarantee and leaves the --- responsibility up to you. If you don't fully consume the --- 'Enumerator' you get here, the next HTTP request in the pipeline --- (if any) will misparse. Be careful with exception handlers. -unsafeDetachRequestBody :: Snap (Enumerator a) -unsafeDetachRequestBody = do - req <- getRequest - let ioref = rqBody req - senum <- liftIO $ readIORef ioref - let (SomeEnumerator enum) = senum - liftIO $ writeIORef ioref - (SomeEnumerator $ return . Iter.joinI . Iter.take 0) - return enum - - ------------------------------------------------------------------------------- --- | Short-circuits a 'Snap' monad action early, storing the given --- 'Response' value in its state. -finishWith :: Response -> Snap () -finishWith = Snap . return . Just . Left -{-# INLINE finishWith #-} - - ------------------------------------------------------------------------------- --- | Fails out of a 'Snap' monad action. This is used to indicate --- that you choose not to handle the given request within the given --- handler. -pass :: Snap a -pass = empty - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only if the request's HTTP method matches --- the given method. -method :: Method -> Snap a -> Snap a -method m action = do - req <- getRequest - unless (rqMethod req == m) pass - action -{-# INLINE method #-} - - ------------------------------------------------------------------------------- --- Appends n bytes of the path info to the context path with a --- trailing slash. -updateContextPath :: Int -> Request -> Request -updateContextPath n req | n > 0 = req { rqContextPath = ctx - , rqPathInfo = pinfo } - | otherwise = req - where - ctx' = S.take n (rqPathInfo req) - ctx = S.concat [rqContextPath req, ctx', "/"] - pinfo = S.drop (n+1) (rqPathInfo req) - - ------------------------------------------------------------------------------- --- Runs a 'Snap' monad action only if the 'rqPathInfo' matches the given --- predicate. -pathWith :: (ByteString -> ByteString -> Bool) - -> ByteString - -> Snap a - -> Snap a -pathWith c p action = do - req <- getRequest - unless (c p (rqPathInfo req)) pass - localRequest (updateContextPath $ S.length p) action - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when the 'rqPathInfo' of the request --- starts with the given path. For example, --- --- > dir "foo" handler --- --- Will fail if 'rqPathInfo' is not \"@\/foo@\" or \"@\/foo\/...@\", and will --- add @\"foo\/\"@ to the handler's local 'rqContextPath'. -dir :: ByteString -- ^ path component to match - -> Snap a -- ^ handler to run - -> Snap a -dir = pathWith f - where - f dr pinfo = dr == x - where - (x,_) = S.break (=='/') pinfo -{-# INLINE dir #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is exactly --- equal to the given string. If the path matches, locally sets 'rqContextPath' --- to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\", and runs the given --- handler. -path :: ByteString -- ^ path to match against - -> Snap a -- ^ handler to run - -> Snap a -path = pathWith (==) -{-# INLINE path #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when 'rqPathInfo' is empty. -ifTop :: Snap a -> Snap a -ifTop = path "" -{-# INLINE ifTop #-} - - ------------------------------------------------------------------------------- --- | Local Snap version of 'get'. -sget :: Snap SnapState -sget = Snap $ liftM (Just . Right) get -{-# INLINE sget #-} - - ------------------------------------------------------------------------------- --- | Local Snap monad version of 'modify'. -smodify :: (SnapState -> SnapState) -> Snap () -smodify f = Snap $ modify f >> return (Just $ Right ()) -{-# INLINE smodify #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Request' object out of the 'Snap' monad. -getRequest :: Snap Request -getRequest = liftM _snapRequest sget -{-# INLINE getRequest #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Response' object out of the 'Snap' monad. -getResponse :: Snap Response -getResponse = liftM _snapResponse sget -{-# INLINE getResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Response' object into the 'Snap' monad. -putResponse :: Response -> Snap () -putResponse r = smodify $ \ss -> ss { _snapResponse = r } -{-# INLINE putResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Request' object into the 'Snap' monad. -putRequest :: Request -> Snap () -putRequest r = smodify $ \ss -> ss { _snapRequest = r } -{-# INLINE putRequest #-} - - ------------------------------------------------------------------------------- --- | Modifies the 'Request' object stored in a 'Snap' monad. -modifyRequest :: (Request -> Request) -> Snap () -modifyRequest f = smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss } -{-# INLINE modifyRequest #-} - - ------------------------------------------------------------------------------- --- | Modifes the 'Response' object stored in a 'Snap' monad. -modifyResponse :: (Response -> Response) -> Snap () -modifyResponse f = smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss } -{-# INLINE modifyResponse #-} - - ------------------------------------------------------------------------------- --- | Log an error message in the 'Snap' monad -logError :: ByteString -> Snap () -logError s = Snap $ gets _snapLogError >>= (\l -> liftIO $ l s) - >> return (Just $ Right ()) -{-# INLINE logError #-} - - ------------------------------------------------------------------------------- --- | Adds the output from the given enumerator to the 'Response' --- stored in the 'Snap' monad state. -addToOutput :: (forall a . Enumerator a) -- ^ output to add - -> Snap () -addToOutput enum = modifyResponse $ modifyResponseBody (>. enum) - - ------------------------------------------------------------------------------- --- | Adds the given strict 'ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeBS :: ByteString -> Snap () -writeBS s = addToOutput $ enumBS s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeLBS :: L.ByteString -> Snap () -writeLBS s = addToOutput $ enumLBS s - - ------------------------------------------------------------------------------- --- | Adds the given strict 'T.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeText :: T.Text -> Snap () -writeText s = writeBS $ T.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'LT.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeLazyText :: LT.Text -> Snap () -writeLazyText s = writeLBS $ LT.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Sets the output to be the contents of the specified file. --- --- Calling 'sendFile' will overwrite any output queued to be sent in the --- 'Response'. If the response body is not modified after the call to --- 'sendFile', Snap will use the efficient @sendfile()@ system call on --- platforms that support it. --- --- If the response body is modified (using 'modifyResponseBody'), the file will --- be read using @mmap()@. -sendFile :: FilePath -> Snap () -sendFile f = modifyResponse $ \r -> r { rspBody = SendFile f } - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' action with a locally-modified 'Request' state --- object. The 'Request' object in the Snap monad state after the call --- to localRequest will be unchanged. -localRequest :: (Request -> Request) -> Snap a -> Snap a -localRequest f m = do - req <- getRequest - - runAct req <|> (putRequest req >> pass) - - where - runAct req = do - modifyRequest f - result <- m - putRequest req - return result -{-# INLINE localRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Request' from state and hands it to the given action. -withRequest :: (Request -> Snap a) -> Snap a -withRequest = (getRequest >>=) -{-# INLINE withRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Response' from state and hands it to the given action. -withResponse :: (Response -> Snap a) -> Snap a -withResponse = (getResponse >>=) -{-# INLINE withResponse #-} - - ------------------------------------------------------------------------------- --- | This exception is thrown if the handler you supply to 'runSnap' fails. -data NoHandlerException = NoHandlerException - deriving (Eq, Typeable) - - ------------------------------------------------------------------------------- -instance Show NoHandlerException where - show NoHandlerException = "No handler for request" - - ------------------------------------------------------------------------------- -instance Exception NoHandlerException - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action in the 'Iteratee IO' monad. -runSnap :: Snap a - -> (ByteString -> IO ()) - -> Request - -> Iteratee IO (Request,Response) -runSnap (Snap m) logerr req = do - (r, ss') <- runStateT m ss - - e <- maybe (return $ Left fourohfour) - return - r - - -- is this a case of early termination? - let resp = case e of - Left x -> x - Right _ -> _snapResponse ss' - - return (_snapRequest ss', resp) - - where - fourohfour = setContentLength 3 $ - setResponseStatus 404 "Not Found" $ - modifyResponseBody (>. enumBS "404") $ - emptyResponse - - dresp = emptyResponse { rspHttpVersion = rqVersion req } - - ss = SnapState req dresp logerr -{-# INLINE runSnap #-} - - ------------------------------------------------------------------------------- -evalSnap :: Snap a - -> (ByteString -> IO ()) - -> Request - -> Iteratee IO a -evalSnap (Snap m) logerr req = do - (r, _) <- runStateT m ss - - e <- maybe (liftIO $ throwIO NoHandlerException) - return - r - - -- is this a case of early termination? - case e of - Left _ -> liftIO $ throwIO $ ErrorCall "no value" - Right x -> return x - where - dresp = emptyResponse { rspHttpVersion = rqVersion req } - ss = SnapState req dresp logerr -{-# INLINE evalSnap #-} - - - ------------------------------------------------------------------------------- --- | See 'rqParam'. Looks up a value for the given named parameter in the --- 'Request'. If more than one value was entered for the given parameter name, --- 'getParam' gloms the values together with: --- --- @ 'S.intercalate' \" \"@ --- -getParam :: ByteString -- ^ parameter name to look up - -> Snap (Maybe ByteString) -getParam k = do - rq <- getRequest - return $ liftM (S.intercalate " ") $ rqParam k rq - - -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Iteratee.html b/static/docs/0.2.1/snap-core/src/Snap-Iteratee.html deleted file mode 100644 index 0aecc98..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Iteratee.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} - --- | Snap Framework type aliases and utilities for iteratees. Note that as a --- convenience, this module also exports everything from @Data.Iteratee@ in the --- @iteratee@ library. --- --- /WARNING/: Note that all of these types are scheduled to change in the --- @darcs@ head version of the @iteratee@ library; John Lato et al. are working --- on a much improved iteratee formulation. - -module Snap.Iteratee - ( -- * Convenience aliases around types from @Data.Iteratee@ - Stream - , IterV - , Iteratee - , Enumerator - - -- * Re-export types and functions from @Data.Iteratee@ - , module Data.Iteratee - - -- * Helper functions - - -- ** Enumerators - , enumBS - , enumLBS - , enumFile - - -- ** Conversion to/from 'WrappedByteString' - , fromWrap - , toWrap - - -- ** Iteratee utilities - , takeExactly - , takeNoMoreThan - , countBytes - , bufferIteratee - ) where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad -import Control.Monad.CatchIO -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Lazy as L -import Data.Iteratee -import qualified Data.Iteratee.Base.StreamChunk as SC -import Data.Iteratee.WrappedByteString -import Data.Monoid (mappend) -import Data.Word (Word8) -import Prelude hiding (catch,drop) -import System.IO.Posix.MMap -import qualified Data.DList as D ------------------------------------------------------------------------------- - -type Stream = StreamG WrappedByteString Word8 -type IterV m = IterGV WrappedByteString Word8 m -type Iteratee m = IterateeG WrappedByteString Word8 m -type Enumerator m a = Iteratee m a -> m (Iteratee m a) - - ------------------------------------------------------------------------------- -instance (Functor m, MonadCatchIO m) => - MonadCatchIO (IterateeG s el m) where - --catch :: Exception e => m a -> (e -> m a) -> m a - catch m handler = IterateeG $ \str -> do - ee <- try $ runIter m str - case ee of - (Left e) -> runIter (handler e) str - (Right v) -> return v - - --block :: m a -> m a - block m = IterateeG $ \str -> block $ runIter m str - unblock m = IterateeG $ \str -> unblock $ runIter m str - - ------------------------------------------------------------------------------- --- | Wraps an 'Iteratee', counting the number of bytes consumed by it. -countBytes :: (Monad m) => Iteratee m a -> Iteratee m (a, Int) -countBytes = go 0 - where - go !n iter = IterateeG $ f n iter - - f !n !iter ch@(Chunk ws) = do - iterv <- runIter iter ch - case iterv of - Done x rest -> let !n' = n + m - len rest - in return $! Done (x, n') rest - Cont i err -> return $ Cont ((go $! n + m) i) err - where - m = S.length $ unWrap ws - - len (EOF _) = 0 - len (Chunk s) = S.length $ unWrap s - - f !n !iter stream = do - iterv <- runIter iter stream - case iterv of - Done x rest -> return $ Done (x, n) rest - Cont i err -> return $ Cont (go n i) err - - ------------------------------------------------------------------------------- --- | Buffers an iteratee. --- --- Our enumerators produce a lot of little strings; rather than spending all --- our time doing kernel context switches for 4-byte write() calls, we buffer --- the iteratee to send 2KB at a time. -bufferIteratee :: (Monad m) => Enumerator m a -bufferIteratee = return . go (D.empty,0) - where - blocksize = 2048 - - --go :: (DList ByteString, Int) -> Iteratee m a -> Iteratee m a - go (!dl,!n) iter = IterateeG $! f (dl,n) iter - - --f :: (DList ByteString, Int) -> Iteratee m a -> Stream -> m (IterV m a) - f _ !iter ch@(EOF (Just _)) = runIter iter ch - f (!dl,_) !iter ch@(EOF Nothing) = do - iterv <- runIter iter $ Chunk big - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> runIter i ch - where - big = toWrap $ L.fromChunks [S.concat $ D.toList dl] - - f (!dl,!n) iter (Chunk ws) = - if n' > blocksize - then do - iterv <- runIter iter (Chunk big) - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> return $ Cont (go (D.empty,0) i) Nothing - else return $ Cont (go (dl',n') iter) Nothing - where - s = S.concat $ L.toChunks $ fromWrap ws - m = S.length s - n' = n+m - dl' = D.snoc dl s - big = toWrap $ L.fromChunks [S.concat $ D.toList dl'] - - ------------------------------------------------------------------------------- --- | Enumerates a strict bytestring. -enumBS :: (Monad m) => ByteString -> Enumerator m a -enumBS bs = enumPure1Chunk $ WrapBS bs -{-# INLINE enumBS #-} - - ------------------------------------------------------------------------------- --- | Enumerates a lazy bytestring. -enumLBS :: (Monad m) => L.ByteString -> Enumerator m a -enumLBS lbs iter = foldM k iter enums - where - enums = map (enumPure1Chunk . WrapBS) $ L.toChunks lbs - k i e = e i - - ------------------------------------------------------------------------------- --- | Converts a lazy bytestring to a wrapped bytestring. -toWrap :: L.ByteString -> WrappedByteString Word8 -toWrap = WrapBS . S.concat . L.toChunks -{-# INLINE toWrap #-} - - ------------------------------------------------------------------------------- --- | Converts a wrapped bytestring to a lazy bytestring. -fromWrap :: WrappedByteString Word8 -> L.ByteString -fromWrap = L.fromChunks . (:[]) . unWrap -{-# INLINE fromWrap #-} - - ------------------------------------------------------------------------------- --- | Reads n elements from a stream and applies the given iteratee to --- the stream of the read elements. Reads exactly n elements, and if --- the stream is short propagates an error. -takeExactly :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeExactly 0 iter = return iter -takeExactly n' iter = - if n' < 0 - then takeExactly 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeExactly n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - step n (Chunk str) = done (Chunk s1) (Chunk s2) - where (s1, s2) = SC.splitAt n str - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n (EOF Nothing) = return $ Cont undefined (Just (Err "short write")) - check n (Done x _) = drop n >> return (return x) - check n (Cont x Nothing) = takeExactly n x - check n (Cont _ (Just e)) = drop n >> throwErr e - done s1 s2 = liftM (flip Done s2) (runIter iter s1 >>= checkIfDone return) - - ------------------------------------------------------------------------------- --- | Reads up to n elements from a stream and applies the given iteratee to the --- stream of the read elements. If more than n elements are read, propagates an --- error. -takeNoMoreThan :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeNoMoreThan n' iter = - if n' < 0 - then takeNoMoreThan 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeNoMoreThan n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - | otherwise = done (Chunk s1) (Chunk s2) - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - (s1, s2) = SC.splitAt n str - - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n chk@(EOF Nothing) = do - v <- runIter iter chk - - case v of - (Done x s) -> return $ Done (return x) s - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont _ Nothing) -> return $ Cont (throwErr $ Err "premature EOF") Nothing - - check _ v@(Done _ _) = return $ liftI v - check n (Cont x Nothing) = takeNoMoreThan n x - check _ (Cont _ (Just e)) = throwErr e - - done _ (EOF _) = error "impossible" - done s1 s2@(Chunk s2') = do - v <- runIter iter s1 - case v of - (Done x s') -> return $ Done (return x) (s' `mappend` s2) - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont i Nothing) -> - if SC.null s2' - then return $ Cont (takeNoMoreThan 0 i) Nothing - else return $ Cont undefined (Just $ Err "too many bytes") - - ------------------------------------------------------------------------------- -enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a) -enumFile fp iter = do - es <- (try $ - liftM WrapBS $ - unsafeMMapFile fp) :: IO (Either SomeException (WrappedByteString Word8)) - - case es of - (Left e) -> return $ throwErr $ Err $ "IO error" ++ show e - (Right s) -> liftM liftI $ runIter iter $ Chunk s -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Types.html b/static/docs/0.2.1/snap-core/src/Snap-Types.html deleted file mode 100644 index ecbe5f0..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Types.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - -
{-| - -This module contains the core type definitions, class instances, and functions -for HTTP as well as the 'Snap' monad, which is used for web handlers. - --} -module Snap.Types - ( - -- * The Snap Monad - Snap - , runSnap - , NoHandlerException(..) - - -- ** Functions for control flow and early termination - , finishWith - , pass - - -- ** Routing - , method - , path - , dir - , ifTop - , route - , routeLocal - - -- ** Access to state - , getRequest - , getResponse - , putRequest - , putResponse - , modifyRequest - , modifyResponse - , localRequest - , withRequest - , withResponse - - -- ** Logging - , logError - - -- ** Grabbing request bodies - , runRequestBody - , getRequestBody - , unsafeDetachRequestBody - -- * HTTP Datatypes and Functions - -- $httpDoc - -- - , Request - , Response - , Headers - , HasHeaders(..) - , Params - , Method(..) - , Cookie(..) - , HttpVersion - - -- ** Headers - , addHeader - , setHeader - , getHeader - - -- ** Requests - , rqServerName - , rqServerPort - , rqRemoteAddr - , rqRemotePort - , rqLocalAddr - , rqLocalHostname - , rqIsSecure - , rqContentLength - , rqMethod - , rqVersion - , rqCookies - , rqPathInfo - , rqContextPath - , rqURI - , rqQueryString - , rqParams - , rqParam - , getParam - , rqModifyParams - , rqSetParam - - -- ** Responses - , emptyResponse - , setResponseStatus - , rspStatus - , rspStatusReason - , setContentType - , addCookie - , setContentLength - , clearContentLength - - -- *** Response I/O - , setResponseBody - , modifyResponseBody - , addToOutput - , writeBS - , writeLazyText - , writeText - , writeLBS - , sendFile - - -- * Iteratee - , Enumerator - - -- * HTTP utilities - , formatHttpTime - , parseHttpTime - , urlEncode - , urlDecode - ) where - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Routing -import Snap.Internal.Types ------------------------------------------------------------------------------- - --- $httpDoc --- HTTP-related datatypes: 'Request', 'Response', 'Cookie', etc. -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Util-FileServe.html b/static/docs/0.2.1/snap-core/src/Snap-Util-FileServe.html deleted file mode 100644 index 38c9a3f..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Util-FileServe.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | Contains web handlers to serve files from a directory. -module Snap.Util.FileServe -( - getSafePath -, fileServe -, fileServe' -, fileServeSingle -, fileServeSingle' -, defaultMimeTypes -, MimeMap -) where - ------------------------------------------------------------------------------- -import Control.Monad -import Control.Monad.Trans -import qualified Data.ByteString.Char8 as S -import Data.ByteString.Char8 (ByteString) -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import System.Directory -import System.FilePath -import System.Posix.Files - ------------------------------------------------------------------------------- -import Snap.Types - - ------------------------------------------------------------------------------- --- | A type alias for MIME type -type MimeMap = Map FilePath ByteString - - ------------------------------------------------------------------------------- --- | The default set of mime type mappings we use when serving files. Its --- value: --- --- > Map.fromList [ --- > ( ".asc" , "text/plain" ), --- > ( ".asf" , "video/x-ms-asf" ), --- > ( ".asx" , "video/x-ms-asf" ), --- > ( ".avi" , "video/x-msvideo" ), --- > ( ".bz2" , "application/x-bzip" ), --- > ( ".c" , "text/plain" ), --- > ( ".class" , "application/octet-stream" ), --- > ( ".conf" , "text/plain" ), --- > ( ".cpp" , "text/plain" ), --- > ( ".css" , "text/css" ), --- > ( ".cxx" , "text/plain" ), --- > ( ".dtd" , "text/xml" ), --- > ( ".dvi" , "application/x-dvi" ), --- > ( ".gif" , "image/gif" ), --- > ( ".gz" , "application/x-gzip" ), --- > ( ".hs" , "text/plain" ), --- > ( ".htm" , "text/html" ), --- > ( ".html" , "text/html" ), --- > ( ".jar" , "application/x-java-archive" ), --- > ( ".jpeg" , "image/jpeg" ), --- > ( ".jpg" , "image/jpeg" ), --- > ( ".js" , "text/javascript" ), --- > ( ".log" , "text/plain" ), --- > ( ".m3u" , "audio/x-mpegurl" ), --- > ( ".mov" , "video/quicktime" ), --- > ( ".mp3" , "audio/mpeg" ), --- > ( ".mpeg" , "video/mpeg" ), --- > ( ".mpg" , "video/mpeg" ), --- > ( ".ogg" , "application/ogg" ), --- > ( ".pac" , "application/x-ns-proxy-autoconfig" ), --- > ( ".pdf" , "application/pdf" ), --- > ( ".png" , "image/png" ), --- > ( ".ps" , "application/postscript" ), --- > ( ".qt" , "video/quicktime" ), --- > ( ".sig" , "application/pgp-signature" ), --- > ( ".spl" , "application/futuresplash" ), --- > ( ".swf" , "application/x-shockwave-flash" ), --- > ( ".tar" , "application/x-tar" ), --- > ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), --- > ( ".tar.gz" , "application/x-tgz" ), --- > ( ".tbz" , "application/x-bzip-compressed-tar" ), --- > ( ".text" , "text/plain" ), --- > ( ".tgz" , "application/x-tgz" ), --- > ( ".torrent" , "application/x-bittorrent" ), --- > ( ".txt" , "text/plain" ), --- > ( ".wav" , "audio/x-wav" ), --- > ( ".wax" , "audio/x-ms-wax" ), --- > ( ".wma" , "audio/x-ms-wma" ), --- > ( ".wmv" , "video/x-ms-wmv" ), --- > ( ".xbm" , "image/x-xbitmap" ), --- > ( ".xml" , "text/xml" ), --- > ( ".xpm" , "image/x-xpixmap" ), --- > ( ".xwd" , "image/x-xwindowdump" ), --- > ( ".zip" , "application/zip" ) ] --- -defaultMimeTypes :: MimeMap -defaultMimeTypes = Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".ttf" , "application/x-font-truetype" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - ------------------------------------------------------------------------------- --- | Gets a path from the 'Request' using 'rqPathInfo' and makes sure it is --- safe to use for opening files. A path is safe if it is a relative path --- and has no ".." elements to escape the intended directory structure. -getSafePath :: Snap FilePath -getSafePath = do - req <- getRequest - let p = S.unpack $ rqPathInfo req - - -- check that we don't have any sneaky .. paths - let dirs = splitDirectories p - when (elem ".." dirs) pass - return p - - ------------------------------------------------------------------------------- --- | Serves files out of the given directory. The relative path given in --- 'rqPathInfo' is searched for the given file, and the file is served with the --- appropriate mime type if it is found. Absolute paths and \"@..@\" are prohibited --- to prevent files from being served from outside the sandbox. --- --- Uses 'defaultMimeTypes' to determine the @Content-Type@ based on the file's --- extension. -fileServe :: FilePath -- ^ root directory - -> Snap () -fileServe = fileServe' defaultMimeTypes -{-# INLINE fileServe #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServe', with control over the MIME mapping used. -fileServe' :: MimeMap -- ^ MIME type mapping - -> FilePath -- ^ root directory - -> Snap () -fileServe' mm root = do - sp <- getSafePath - let fp = root </> sp - - -- check that the file exists - liftIO (doesFileExist fp) >>= flip unless pass - - let fn = takeFileName fp - let mime = fileType mm fn - fileServeSingle' mime fp -{-# INLINE fileServe' #-} - - ------------------------------------------------------------------------------- --- | Serves a single file specified by a full or relative path. The --- path restrictions on fileServe don't apply to this function since --- the path is not being supplied by the user. -fileServeSingle :: FilePath -- ^ path to file - -> Snap () -fileServeSingle fp = - fileServeSingle' (fileType defaultMimeTypes (takeFileName fp)) fp -{-# INLINE fileServeSingle #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServeSingle', with control over the MIME mapping used. -fileServeSingle' :: ByteString -- ^ MIME type mapping - -> FilePath -- ^ path to file - -> Snap () -fileServeSingle' mime fp = do - req <- getRequest - - let mbH = getHeader "if-modified-since" req - mbIfModified <- liftIO $ case mbH of - Nothing -> return Nothing - (Just s) -> liftM Just $ parseHttpTime s - - -- check modification time and bug out early if the file is not modified. - filestat <- liftIO $ getFileStatus fp - let mt = modificationTime filestat - maybe (return ()) (chkModificationTime mt) mbIfModified - - let sz = fromEnum $ fileSize filestat - lm <- liftIO $ formatHttpTime mt - - modifyResponse $ setHeader "Last-Modified" lm - . setContentType mime - . setContentLength sz - sendFile fp - - where - -------------------------------------------------------------------------- - chkModificationTime mt lt = when (mt <= lt) notModified - - -------------------------------------------------------------------------- - notModified = finishWith $ - setResponseStatus 304 "Not Modified" emptyResponse - - ------------------------------------------------------------------------------- -fileType :: MimeMap -> FilePath -> ByteString -fileType mm f = - if null ext - then defaultMimeType - else fromMaybe (fileType mm (drop 1 ext)) - mbe - - where - ext = takeExtensions f - mbe = Map.lookup ext mm - - ------------------------------------------------------------------------------- -defaultMimeType :: ByteString -defaultMimeType = "application/octet-stream" -- diff --git a/static/docs/0.2.1/snap-core/src/Snap-Util-GZip.html b/static/docs/0.2.1/snap-core/src/Snap-Util-GZip.html deleted file mode 100644 index 6119f1d..0000000 --- a/static/docs/0.2.1/snap-core/src/Snap-Util-GZip.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Util.GZip -( withCompression -, withCompression' ) where - -import qualified Codec.Compression.GZip as GZip -import qualified Codec.Compression.Zlib as Zlib -import Control.Concurrent -import Control.Applicative hiding (many) -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.Attoparsec.Char8 hiding (Done) -import qualified Data.Attoparsec.Char8 as Atto -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Char8 (ByteString) -import Data.Iteratee.WrappedByteString -import Data.Maybe -import qualified Data.Set as Set -import Data.Set (Set) -import Data.Typeable -import Prelude hiding (catch, takeWhile) - ------------------------------------------------------------------------------- -import Snap.Internal.Debug -import Snap.Iteratee hiding (Enumerator) -import Snap.Types - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' web handler with compression if available. --- --- If the client has indicated support for @gzip@ or @compress@ in its --- @Accept-Encoding@ header, and the @Content-Type@ in the response is one of --- the following types: --- --- * @application/x-javascript@ --- --- * @text/css@ --- --- * @text/html@ --- --- * @text/javascript@ --- --- * @text/plain@ --- --- * @text/xml@ --- --- * @application/x-font-truetype@ --- --- Then the given handler's output stream will be compressed, --- @Content-Encoding@ will be set in the output headers, and the --- @Content-Length@ will be cleared if it was set. (We can't process the stream --- in O(1) space if the length is known beforehand.) --- --- The wrapped handler will be run to completion, and then the 'Response' --- that's contained within the 'Snap' monad state will be passed to --- 'finishWith' to prevent further processing. --- -withCompression :: Snap a -- ^ the web handler to run - -> Snap () -withCompression = withCompression' compressibleMimeTypes - - ------------------------------------------------------------------------------- --- | The same as 'withCompression', with control over which MIME types to --- compress. -withCompression' :: Set ByteString - -- ^ set of compressible MIME types - -> Snap a - -- ^ the web handler to run - -> Snap () -withCompression' mimeTable action = do - _ <- action - resp <- getResponse - - -- If a content-encoding is already set, do nothing. This prevents - -- "withCompression $ withCompression m" from ruining your day. - if isJust $ getHeader "Content-Encoding" resp - then return () - else do - let mbCt = getHeader "Content-Type" resp - - debug $ "withCompression', content-type is " ++ show mbCt - - case mbCt of - (Just ct) -> if Set.member ct mimeTable - then chkAcceptEncoding - else return () - _ -> return () - - - getResponse >>= finishWith - - where - chkAcceptEncoding :: Snap () - chkAcceptEncoding = do - req <- getRequest - debug $ "checking accept-encoding" - let mbAcc = getHeader "Accept-Encoding" req - debug $ "accept-encoding is " ++ show mbAcc - let s = fromMaybe "" mbAcc - - types <- liftIO $ parseAcceptEncoding s - - chooseType types - - - chooseType [] = return () - chooseType ("gzip":_) = gzipCompression - chooseType ("compress":_) = compressCompression - chooseType ("x-gzip":_) = gzipCompression - chooseType ("x-compress":_) = compressCompression - chooseType (_:xs) = chooseType xs - - ------------------------------------------------------------------------------- --- private following ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -compressibleMimeTypes :: Set ByteString -compressibleMimeTypes = Set.fromList [ "application/x-font-truetype" - , "application/x-javascript" - , "text/css" - , "text/html" - , "text/javascript" - , "text/plain" - , "text/xml" ] - - - - ------------------------------------------------------------------------------- -gzipCompression :: Snap () -gzipCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "gzip" . - clearContentLength . - modifyResponseBody gcompress - - ------------------------------------------------------------------------------- -compressCompression :: Snap () -compressCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "compress" . - clearContentLength . - modifyResponseBody ccompress - - ------------------------------------------------------------------------------- -gcompress :: forall a . Enumerator a -> Enumerator a -gcompress = compressEnumerator GZip.compress - - ------------------------------------------------------------------------------- -ccompress :: forall a . Enumerator a -> Enumerator a -ccompress = compressEnumerator Zlib.compress - - ------------------------------------------------------------------------------- -compressEnumerator :: forall a . - (L.ByteString -> L.ByteString) - -> Enumerator a - -> Enumerator a -compressEnumerator compFunc enum iteratee = do - writeEnd <- newChan - readEnd <- newChan - tid <- forkIO $ threadProc readEnd writeEnd - - enum (IterateeG $ f readEnd writeEnd tid iteratee) - - where - -------------------------------------------------------------------------- - streamFinished :: Stream -> Bool - streamFinished (EOF _) = True - streamFinished (Chunk _) = False - - - -------------------------------------------------------------------------- - consumeSomeOutput :: Chan Stream - -> Iteratee IO a - -> IO (Iteratee IO a) - consumeSomeOutput writeEnd iter = do - e <- isEmptyChan writeEnd - if e - then return iter - else do - ch <- readChan writeEnd - - iter' <- liftM liftI $ runIter iter ch - if (streamFinished ch) - then return iter' - else consumeSomeOutput writeEnd iter' - - - -------------------------------------------------------------------------- - consumeRest :: Chan Stream - -> Iteratee IO a - -> IO (IterV IO a) - consumeRest writeEnd iter = do - ch <- readChan writeEnd - - iv <- runIter iter ch - if (streamFinished ch) - then return iv - else consumeRest writeEnd $ liftI iv - - - -------------------------------------------------------------------------- - f readEnd writeEnd tid i (EOF Nothing) = do - writeChan readEnd Nothing - x <- consumeRest writeEnd i - killThread tid - return x - - f _ _ tid i ch@(EOF (Just _)) = do - x <- runIter i ch - killThread tid - return x - - f readEnd writeEnd tid i (Chunk s') = do - let s = unWrap s' - writeChan readEnd $ Just s - i' <- consumeSomeOutput writeEnd i - return $ Cont (IterateeG $ f readEnd writeEnd tid i') Nothing - - - -------------------------------------------------------------------------- - threadProc :: Chan (Maybe ByteString) - -> Chan Stream - -> IO () - threadProc readEnd writeEnd = do - stream <- getChanContents readEnd - let bs = L.fromChunks $ streamToChunks stream - - let output = L.toChunks $ compFunc bs - let runIt = do - mapM_ (writeChan writeEnd . toChunk) output - writeChan writeEnd $ EOF Nothing - - runIt `catch` \(e::SomeException) -> - writeChan writeEnd $ EOF (Just $ Err $ show e) - - - -------------------------------------------------------------------------- - streamToChunks [] = [] - streamToChunks (Nothing:_) = [] - streamToChunks ((Just x):xs) = x:(streamToChunks xs) - - - -------------------------------------------------------------------------- - toChunk = Chunk . WrapBS - - ------------------------------------------------------------------------------- -fullyParse :: ByteString -> Parser a -> Either String a -fullyParse s p = - case r' of - (Fail _ _ e) -> Left e - (Partial _) -> Left "parse failed" - (Atto.Done _ x) -> Right x - where - r = parse p s - r' = feed r "" - - ------------------------------------------------------------------------------- --- We're not gonna bother with quality values; we'll do gzip or compress in --- that order. -acceptParser :: Parser [ByteString] -acceptParser = do - xs <- option [] $ (:[]) <$> encoding - ys <- many (char ',' *> encoding) - endOfInput - return $ xs ++ ys - where - encoding = skipSpace *> c <* skipSpace - - c = do - x <- coding - option () qvalue - return x - - qvalue = do - skipSpace - char ';' - skipSpace - char 'q' - skipSpace - char '=' - float - return () - - coding = string "*" <|> takeWhile isCodingChar - - isCodingChar c = isAlpha_ascii c || c == '-' - - float = takeWhile isDigit >> - option () (char '.' >> takeWhile isDigit >> pure ()) - - ------------------------------------------------------------------------------- -data BadAcceptEncodingException = BadAcceptEncodingException - deriving (Typeable) - - ------------------------------------------------------------------------------- -instance Show BadAcceptEncodingException where - show BadAcceptEncodingException = "bad 'accept-encoding' header" - - ------------------------------------------------------------------------------- -instance Exception BadAcceptEncodingException - - ------------------------------------------------------------------------------- -parseAcceptEncoding :: ByteString -> IO [ByteString] -parseAcceptEncoding s = - case r of - Left _ -> throwIO BadAcceptEncodingException - Right x -> return x - where - r = fullyParse s acceptParser - -- diff --git a/static/docs/0.2.1/snap-core/src/hscolour.css b/static/docs/0.2.1/snap-core/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.2.1/snap-core/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.2.1/snap-server/Snap-Http-Server-Config.html b/static/docs/0.2.1/snap-server/Snap-Http-Server-Config.html deleted file mode 100644 index 42ca9f0..0000000 --- a/static/docs/0.2.1/snap-server/Snap-Http-Server-Config.html +++ /dev/null @@ -1,263 +0,0 @@ - - -
| ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
data Config | ||||||||||||||||||
| ||||||||||||||||||
readConfigFromCmdLineArgs | ||||||||||||||||||
| ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||
| ||||||||||||||||
Description | ||||||||||||||||
The Snap HTTP server is a high performance, epoll-enabled, iteratee-based - web server library written in Haskell. Together with the snap-core library - upon which it depends, it provides a clean and efficient Haskell programming - interface to the HTTP protocol. - | ||||||||||||||||
Synopsis | ||||||||||||||||
| ||||||||||||||||
Documentation | ||||||||||||||||
httpServe | ||||||||||||||||
| ||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||
| ||||||||||||||||||
Synopsis | ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
data Logger | ||||||||||||||||||
| ||||||||||||||||||
timestampedLogEntry :: ByteString -> IO ByteString | ||||||||||||||||||
Prepares a log message with the time prepended. - | ||||||||||||||||||
combinedLogEntry | ||||||||||||||||||
| ||||||||||||||||||
newLogger :: FilePath -> IO Logger | ||||||||||||||||||
Creates a new logger, logging to the given file. If the file argument is - "-", then log to stdout; if it's "stderr" then we log to stderr, - otherwise we log to a regular file in append mode. The file is closed and - re-opened every 15 minutes to facilitate external log rotation. - | ||||||||||||||||||
logMsg :: Logger -> ByteString -> IO () | ||||||||||||||||||
Sends out a log message verbatim with a newline appended. Note: - if you want a fancy log message you'll have to format it yourself - (or use combinedLogEntry). - | ||||||||||||||||||
stopLogger :: Logger -> IO () | ||||||||||||||||||
Kills a logger thread, causing any unwritten contents to be - flushed out to disk - | ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||
snap-server-0.2.1: A fast, iteratee-based, epoll-enabled web server for the Snap Framework | ||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - The Snap HTTP server is a high performance, epoll-enabled, iteratee-based web -server library written in Haskell. Together with the snap-core library upon -which it depends, it provides a clean and efficient Haskell programming -interface to the HTTP protocol. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the apply splice. - | |||||
| |||||
Default attribute name for the apply tag. - | |||||
| |||||
Implementation of the apply splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the bind splice. - | |||||
| |||||
Default attribute name for the bind tag. - | |||||
| |||||
Implementation of the bind splice. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
Default name for the ignore splice. - | |||||
| |||||
The ignore tag and everything it surrounds disappears in the - rendered output. - | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Default name for the markdown splice. - | |||||||||||||
| |||||||||||||
Implementation of the markdown splice. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Synopsis | |||||
| |||||
Documentation | |||||
| |||||
| |||||
| |||||
Modifies a TemplateState to include a static tag. - | |||||
| |||||
Clears the static tag state. - | |||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Documentation | |||||
module Text.Templating.Heist.Splices.Apply | |||||
module Text.Templating.Heist.Splices.Bind | |||||
module Text.Templating.Heist.Splices.Ignore | |||||
module Text.Templating.Heist.Splices.Markdown | |||||
module Text.Templating.Heist.Splices.Static | |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core definitions for the Heist template system. - The Heist template system is based on XML/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - The most important concept in Heist is the Splice. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. Splice is implemented as a type synonym type Splice m = - TemplateMonad m [Node], and TemplateMonad has a function getParamNode - that lets you get the input node. - Suppose you have a place on your page where you want to display a link with - the text "Logout username" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - getUser :: MyAppMonad (Maybe ByteString) that gets the current user. - You can implement this functionality with a Splice as follows: - - import Text.XML.Expat.Tree - - link :: ByteString -> ByteString -> Node - link target text = X.Element "a" [("href", target)] [X.Text text] - - loginLink :: Node - loginLink = link "/login" "Login" - - logoutLink :: ByteString -> Node - logoutLink user = link "/logout" (B.append "Logout " user) - - loginLogoutSplice :: Splice MyAppMonad - loginLogoutSplice = do - user <- lift getUser - return $ [maybe loginLink logoutLink user] - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the TemplateState data structure. The - following code demonstrates how this splice would be used. - mySplices = [ ("loginLogout", loginLogoutSplice) ] - - main = do - ets <- loadTemplates "templates" $ - foldr (uncurry bindSplice) emptyTemplateState mySplices - let ts = either error id ets - t <- runMyAppMonad $ renderTemplate ts "index" - print $ maybe "Page not found" id t - Here we build up our TemplateState by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final TemplateState wrapped in an Either to handle - errors. Then we use this TemplateState to render our templates. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Types - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist templates are XML documents. The hexpat library is polymorphic over - the type of strings, so here we define a Node alias to fix the string - types of the tag names and tag bodies to ByteString. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Splice is a TemplateMonad computation that returns [Node]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Template is a forest of XML nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions and declarations on TemplateState values - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a template to the template state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty template state, with Heist's default splices (<bind> and - <apply>) mapped. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Convenience function for looking up a splice. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the templateMap in a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Traverses the specified directory structure and builds a - TemplateState by loading all the files with a .tpl extension. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hook functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heist hooks allow you to modify templates when they are loaded and before - and after they are run. Every time you call one of the addAbcHook - functions the hook is added to onto the processing pipeline. The hooks - processes the template in the order that they were added to the - TemplateState. - The pre-run and post-run hooks are run before and after every template is - run/rendered. You should be careful what code you put in these hooks - because it can significantly affect the performance of your site. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds an on-load hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a pre-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a post-run hook to a TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TemplateMonad functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Stops the recursive processing of splices. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the node currently being processed. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Performs splice processing on a list of nodes. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets the current context - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for running splices and templates - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name in the supplied TemplateState and runs - it in the underlying monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Looks up a template name evaluates it. Same as runTemplate except it - runs in TemplateMonad instead of m. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Renders a template from the specified TemplateState. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Binds a list of constant string splices - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Misc functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a template in the underlying monad. Similar to runSplice - except that templates don't require a Node as a parameter. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reads an XML document from disk. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a TemplateState to include a static tag. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||||||||||
heist-0.1.3: An xhtml templating system | |||||||||||||||||||||||||||||||||||||||
An xhtml templating system - | |||||||||||||||||||||||||||||||||||||||
Modules | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE OverloadedStrings #-} -module Text.Templating.Heist.Constants where - -import Data.ByteString.Char8 (ByteString) -import qualified Data.Map as Map -import Data.Map (Map) - -htmlEntityLookupTable :: Map ByteString ByteString -htmlEntityLookupTable = Map.fromList [ - ("acute" , "\xc2\xb4") - , ("cedil" , "\xc2\xb8") - , ("circ" , "\xcb\x86") - , ("macr" , "\xc2\xaf") - , ("middot" , "\xc2\xb7") - , ("tilde" , "\xcb\x9c") - , ("uml" , "\xc2\xa8") - , ("Aacute" , "\xc3\x81") - , ("aacute" , "\xc3\xa1") - , ("Acirc" , "\xc3\x82") - , ("acirc" , "\xc3\xa2") - , ("AElig" , "\xc3\x86") - , ("aelig" , "\xc3\xa6") - , ("Agrave" , "\xc3\x80") - , ("agrave" , "\xc3\xa0") - , ("Aring" , "\xc3\x85") - , ("aring" , "\xc3\xa5") - , ("Atilde" , "\xc3\x83") - , ("atilde" , "\xc3\xa3") - , ("Auml" , "\xc3\x84") - , ("auml" , "\xc3\xa4") - , ("Ccedil" , "\xc3\x87") - , ("ccedil" , "\xc3\xa7") - , ("Eacute" , "\xc3\x89") - , ("eacute" , "\xc3\xa9") - , ("Ecirc" , "\xc3\x8a") - , ("ecirc" , "\xc3\xaa") - , ("Egrave" , "\xc3\x88") - , ("egrave" , "\xc3\xa8") - , ("ETH" , "\xc3\x90") - , ("eth" , "\xc3\xb0") - , ("Euml" , "\xc3\x8b") - , ("euml" , "\xc3\xab") - , ("Iacute" , "\xc3\x8d") - , ("iacute" , "\xc3\xad") - , ("Icirc" , "\xc3\x8e") - , ("icirc" , "\xc3\xae") - , ("Igrave" , "\xc3\x8c") - , ("igrave" , "\xc3\xac") - , ("Iuml" , "\xc3\x8f") - , ("iuml" , "\xc3\xaf") - , ("Ntilde" , "\xc3\x91") - , ("ntilde" , "\xc3\xb1") - , ("Oacute" , "\xc3\x93") - , ("oacute" , "\xc3\xb3") - , ("Ocirc" , "\xc3\x94") - , ("ocirc" , "\xc3\xb4") - , ("OElig" , "\xc5\x92") - , ("oelig" , "\xc5\x93") - , ("Ograve" , "\xc3\x92") - , ("ograve" , "\xc3\xb2") - , ("Oslash" , "\xc3\x98") - , ("oslash" , "\xc3\xb8") - , ("Otilde" , "\xc3\x95") - , ("otilde" , "\xc3\xb5") - , ("Ouml" , "\xc3\x96") - , ("ouml" , "\xc3\xb6") - , ("Scaron" , "\xc5\xa0") - , ("scaron" , "\xc5\xa1") - , ("szlig" , "\xc3\x9f") - , ("THORN" , "\xc3\x9e") - , ("thorn" , "\xc3\xbe") - , ("Uacute" , "\xc3\x9a") - , ("uacute" , "\xc3\xba") - , ("Ucirc" , "\xc3\x9b") - , ("ucirc" , "\xc3\xbb") - , ("Ugrave" , "\xc3\x99") - , ("ugrave" , "\xc3\xb9") - , ("Uuml" , "\xc3\x9c") - , ("uuml" , "\xc3\xbc") - , ("Yacute" , "\xc3\x9d") - , ("yacute" , "\xc3\xbd") - , ("yuml" , "\xc3\xbf") - , ("Yuml" , "\xc5\xb8") - , ("cent" , "\xc2\xa2") - , ("curren" , "\xc2\xa4") - , ("euro" , "\xe2\x82\xac") - , ("pound" , "\xc2\xa3") - , ("yen" , "\xc2\xa5") - , ("brvbar" , "\xc2\xa6") - , ("bull" , "\xe2\x80\xa2") - , ("copy" , "\xc2\xa9") - , ("dagger" , "\xe2\x80\xa0") - , ("Dagger" , "\xe2\x80\xa1") - , ("frasl" , "\xe2\x81\x84") - , ("hellip" , "\xe2\x80\xa6") - , ("iexcl" , "\xc2\xa1") - , ("image" , "\xe2\x84\x91") - , ("iquest" , "\xc2\xbf") - , ("mdash" , "\xe2\x80\x94") - , ("ndash" , "\xe2\x80\x93") - , ("not" , "\xc2\xac") - , ("oline" , "\xe2\x80\xbe") - , ("ordf" , "\xc2\xaa") - , ("ordm" , "\xc2\xba") - , ("para" , "\xc2\xb6") - , ("permil" , "\xe2\x80\xb0") - , ("prime" , "\xe2\x80\xb2") - , ("Prime" , "\xe2\x80\xb3") - , ("real" , "\xe2\x84\x9c") - , ("reg" , "\xc2\xae") - , ("sect" , "\xc2\xa7") - , ("shy" , "\173") - , ("sup1" , "\xc2\xb9") - , ("trade" , "\xe2\x84\xa2") - , ("weierp" , "\xe2\x84\x98") - , ("bdquo" , "\xe2\x80\x9e") - , ("laquo" , "\xc2\xab") - , ("ldquo" , "\xe2\x80\x9c") - , ("lsaquo" , "\xe2\x80\xb9") - , ("lsquo" , "\xe2\x80\x98") - , ("raquo" , "\xc2\xbb") - , ("rdquo" , "\xe2\x80\x9d") - , ("rsaquo" , "\xe2\x80\xba") - , ("rsquo" , "\xe2\x80\x99") - , ("sbquo" , "\xe2\x80\x9a") - , ("emsp" , "\xe2\x80\x83") - , ("ensp" , "\xe2\x80\x82") - , ("nbsp" , "\x20") - , ("thinsp" , "\xe2\x80\x89") - , ("zwj" , "\xe2\x80\x8d") - , ("zwnj" , "\xe2\x80\x8c") - , ("deg" , "\xc2\xb0") - , ("divide" , "\xc3\xb7") - , ("frac12" , "\xc2\xbd") - , ("frac14" , "\xc2\xbc") - , ("frac34" , "\xc2\xbe") - , ("ge" , "\xe2\x89\xa5") - , ("le" , "\xe2\x89\xa4") - , ("minus" , "\xe2\x88\x92") - , ("sup2" , "\xc2\xb2") - , ("sup3" , "\xc2\xb3") - , ("times" , "\xc3\x97") - , ("alefsym" , "\xe2\x84\xb5") - , ("and" , "\xe2\x88\xa7") - , ("ang" , "\xe2\x88\xa0") - , ("asymp" , "\xe2\x89\x88") - , ("cap" , "\xe2\x88\xa9") - , ("cong" , "\xe2\x89\x85") - , ("cup" , "\xe2\x88\xaa") - , ("empty" , "\xe2\x88\x85") - , ("equiv" , "\xe2\x89\xa1") - , ("exist" , "\xe2\x88\x83") - , ("fnof" , "\xc6\x92") - , ("forall" , "\xe2\x88\x80") - , ("infin" , "\xe2\x88\x9e") - , ("int" , "\xe2\x88\xab") - , ("isin" , "\xe2\x88\x88") - , ("lang" , "\xe3\x80\x88") - , ("lceil" , "\xe2\x8c\x88") - , ("lfloor" , "\xe2\x8c\x8a") - , ("lowast" , "\xe2\x88\x97") - , ("micro" , "\xc2\xb5") - , ("nabla" , "\xe2\x88\x87") - , ("ne" , "\xe2\x89\xa0") - , ("ni" , "\xe2\x88\x8b") - , ("notin" , "\xe2\x88\x89") - , ("nsub" , "\xe2\x8a\x84") - , ("oplus" , "\xe2\x8a\x95") - , ("or" , "\xe2\x88\xa8") - , ("otimes" , "\xe2\x8a\x97") - , ("part" , "\xe2\x88\x82") - , ("perp" , "\xe2\x8a\xa5") - , ("plusmn" , "\xc2\xb1") - , ("prod" , "\xe2\x88\x8f") - , ("prop" , "\xe2\x88\x9d") - , ("radic" , "\xe2\x88\x9a") - , ("rang" , "\xe3\x80\x89") - , ("rceil" , "\xe2\x8c\x89") - , ("rfloor" , "\xe2\x8c\x8b") - , ("sdot" , "\xe2\x8b\x85") - , ("sim" , "\xe2\x88\xbc") - , ("sub" , "\xe2\x8a\x82") - , ("sube" , "\xe2\x8a\x86") - , ("sum" , "\xe2\x88\x91") - , ("sup" , "\xe2\x8a\x83") - , ("supe" , "\xe2\x8a\x87") - , ("there4" , "\xe2\x88\xb4") - , ("Alpha" , "\xce\x91") - , ("alpha" , "\xce\xb1") - , ("Beta" , "\xce\x92") - , ("beta" , "\xce\xb2") - , ("Chi" , "\xce\xa7") - , ("chi" , "\xcf\x87") - , ("Delta" , "\xce\x94") - , ("delta" , "\xce\xb4") - , ("Epsilon" , "\xce\x95") - , ("epsilon" , "\xce\xb5") - , ("Eta" , "\xce\x97") - , ("eta" , "\xce\xb7") - , ("Gamma" , "\xce\x93") - , ("gamma" , "\xce\xb3") - , ("Iota" , "\xce\x99") - , ("iota" , "\xce\xb9") - , ("Kappa" , "\xce\x9a") - , ("kappa" , "\xce\xba") - , ("Lambda" , "\xce\x9b") - , ("lambda" , "\xce\xbb") - , ("Mu" , "\xce\x9c") - , ("mu" , "\xce\xbc") - , ("Nu" , "\xce\x9d") - , ("nu" , "\xce\xbd") - , ("Omega" , "\xce\xa9") - , ("omega" , "\xcf\x89") - , ("Omicron" , "\xce\x9f") - , ("omicron" , "\xce\xbf") - , ("Phi" , "\xce\xa6") - , ("phi" , "\xcf\x86") - , ("Pi" , "\xce\xa0") - , ("pi" , "\xcf\x80") - , ("piv" , "\xcf\x96") - , ("Psi" , "\xce\xa8") - , ("psi" , "\xcf\x88") - , ("Rho" , "\xce\xa1") - , ("rho" , "\xcf\x81") - , ("Sigma" , "\xce\xa3") - , ("sigma" , "\xcf\x83") - , ("sigmaf" , "\xcf\x82") - , ("Tau" , "\xce\xa4") - , ("tau" , "\xcf\x84") - , ("Theta" , "\xce\x98") - , ("theta" , "\xce\xb8") - , ("thetasym" , "\xcf\x91") - , ("upsih" , "\xcf\x92") - , ("Upsilon" , "\xce\xa5") - , ("upsilon" , "\xcf\x85") - , ("Xi" , "\xce\x9e") - , ("xi" , "\xce\xbe") - , ("Zeta" , "\xce\x96") - , ("zeta" , "\xce\xb6") - , ("crarr" , "\xe2\x86\xb5") - , ("darr" , "\xe2\x86\x93") - , ("dArr" , "\xe2\x87\x93") - , ("harr" , "\xe2\x86\x94") - , ("hArr" , "\xe2\x87\x94") - , ("larr" , "\xe2\x86\x90") - , ("lArr" , "\xe2\x87\x90") - , ("rarr" , "\xe2\x86\x92") - , ("rArr" , "\xe2\x87\x92") - , ("uarr" , "\xe2\x86\x91") - , ("uArr" , "\xe2\x87\x91") - , ("clubs" , "\xe2\x99\xa3") - , ("diams" , "\xe2\x99\xa6") - , ("hearts" , "\xe2\x99\xa5") - , ("spades" , "\xe2\x99\xa0") - , ("loz" , "\xe2\x97\x8a") ] -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Internal.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Internal.html deleted file mode 100644 index 2546209..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Internal.html +++ /dev/null @@ -1,524 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Text.Templating.Heist.Internal where - ------------------------------------------------------------------------------- -import Control.Exception (SomeException) -import Control.Monad.CatchIO -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as L -import Data.Either -import qualified Data.Foldable as F -import Data.List -import qualified Data.Map as Map -import Data.Map (Map) -import Data.Typeable -import Prelude hiding (catch) -import System.Directory.Tree hiding (name) -import Text.XML.Expat.Format -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Constants - ------------------------------------------------------------------------------- --- Types ------------------------------------------------------------------------------- - --- | Heist templates are XML documents. The hexpat library is polymorphic over --- the type of strings, so here we define a 'Node' alias to fix the string --- types of the tag names and tag bodies to 'ByteString'. -type Node = X.Node ByteString ByteString - - ------------------------------------------------------------------------------- --- | A 'Template' is a forest of XML nodes. -type Template = [Node] - - ------------------------------------------------------------------------------- --- | Reversed list of directories -type TPath = [ByteString] - - ------------------------------------------------------------------------------- -type TemplateMap = Map TPath Template - - ------------------------------------------------------------------------------- --- | Holds all the state information needed for template processing: --- --- * a collection of named templates. If you use the @\<apply --- template=\"foo\"\>@ tag to include another template by name, @\"foo\"@ --- is looked up in here. --- --- * the mapping from tag names to 'Splice's. --- --- * a flag to control whether we will recurse during splice processing. --- --- We'll illustrate the recursion flag with a small example template: --- --- > <foo> --- > <bar> --- > ... --- > </bar> --- > </foo> --- --- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ --- splice will result in a list of nodes @L@; if the recursion flag is on we --- will recursively scan @L@ for splices, otherwise @L@ will be included in the --- output verbatim. -data TemplateState m = TemplateState { - -- | A mapping of splice names to splice actions - _spliceMap :: SpliceMap m - -- | A mapping of template names to templates - , _templateMap :: TemplateMap - -- | A flag to control splice recursion - , _recurse :: Bool - , _curContext :: TPath - , _recursionDepth :: Int - , _onLoadHook :: Template -> IO Template - , _preRunHook :: Template -> m Template - , _postRunHook :: Template -> m Template -} - - ------------------------------------------------------------------------------- -instance Eq (TemplateState m) where - a == b = (_recurse a == _recurse b) && - (_templateMap a == _templateMap b) && - (_curContext a == _curContext b) - - ------------------------------------------------------------------------------- --- | 'TemplateMonad' is a monad transformer that gives you access to the 'Node' --- being processed (using the 'MonadReader' instance) as well as holding the --- 'TemplateState' that contains splice and template mappings (accessible --- using the 'MonadState' instance. -newtype TemplateMonad m a = TemplateMonad (RWST Node () (TemplateState m) m a) - deriving ( Monad - , MonadIO - , MonadCatchIO - , MonadReader Node - , MonadState (TemplateState m) ) - - ------------------------------------------------------------------------------- -instance (Monad m) => Monoid (TemplateState m) where - mempty = TemplateState Map.empty Map.empty True [] 0 - return return return - - (TemplateState s1 t1 r1 _ d1 o1 b1 a1) `mappend` - (TemplateState s2 t2 r2 c2 d2 o2 b2 a2) = - TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) - where - s = s1 `mappend` s2 - t = t1 `mappend` t2 - r = r1 && r2 - d = max d1 d2 - - ------------------------------------------------------------------------------- -instance MonadTrans TemplateMonad where - lift = TemplateMonad . lift - ------------------------------------------------------------------------------- -instance (Typeable1 m, Typeable a) => Typeable (TemplateMonad m a) where - typeOf _ = mkTyConApp tCon [mRep, aRep] - where - tCon = mkTyCon "TemplateMonad" - maRep = typeOf (undefined :: m a) - (mCon, [aRep]) = splitTyConApp maRep - mRep = mkTyConApp mCon [] - - ------------------------------------------------------------------------------- --- | A Splice is a TemplateMonad computation that returns [Node]. -type Splice m = TemplateMonad m Template - - ------------------------------------------------------------------------------- --- | SpliceMap associates a name and a Splice. -type SpliceMap m = Map ByteString (Splice m) - - ------------------------------------------------------------------------------- --- TemplateState functions ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- --- | Adds an on-load hook to a `TemplateState`. -addOnLoadHook :: (Monad m) => - (Template -> IO Template) - -> TemplateState m - -> TemplateState m -addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a pre-run hook to a `TemplateState`. -addPreRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Adds a post-run hook to a `TemplateState`. -addPostRunHook :: (Monad m) => - (Template -> m Template) - -> TemplateState m - -> TemplateState m -addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } - - ------------------------------------------------------------------------------- --- | Bind a new splice declaration to a tag name within a 'TemplateState'. -bindSplice :: Monad m => - ByteString -- ^ tag name - -> Splice m -- ^ splice action - -> TemplateState m -- ^ source state - -> TemplateState m -bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a splice. -lookupSplice :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Splice m) -lookupSplice nm ts = Map.lookup nm $ _spliceMap ts - - ------------------------------------------------------------------------------- --- | Converts a path into an array of the elements in reverse order. If the --- path is absolute, we need to remove the leading slash so the split doesn't --- leave @\"\"@ as the last element of the TPath. --- --- FIXME @\"..\"@ currently doesn't work in paths, the solution is non-trivial -splitPaths :: ByteString -> TPath -splitPaths p = if B.null p then [] else (reverse $ B.split '/' path) - where - path = if B.head p == '/' then B.tail p else p - - ------------------------------------------------------------------------------- --- | Does a single template lookup without cascading up. -singleLookup :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm - - ------------------------------------------------------------------------------- --- | Searches for a template by looking in the full path then backing up into each --- of the parent directories until the template is found. -traversePath :: TemplateMap - -> TPath - -> ByteString - -> Maybe (Template, TPath) -traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) -traversePath tm path name = - singleLookup tm path name `mplus` - traversePath tm (tail path) name - - ------------------------------------------------------------------------------- --- | Convenience function for looking up a template. -lookupTemplate :: Monad m => - ByteString - -> TemplateState m - -> Maybe (Template, TPath) -lookupTemplate nameStr ts = - f (_templateMap ts) path name - where (name:p) = case splitPaths nameStr of - [] -> [""] - ps -> ps - path = p ++ (_curContext ts) - f = if '/' `B.elem` nameStr - then singleLookup - else traversePath - - ------------------------------------------------------------------------------- --- | Sets the templateMap in a TemplateState. -setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m -setTemplates m ts = ts { _templateMap = m } - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -insertTemplate :: Monad m => - TPath - -> Template - -> TemplateState m - -> TemplateState m -insertTemplate p t st = - setTemplates (Map.insert p t (_templateMap st)) st - - ------------------------------------------------------------------------------- --- | Adds a template to the template state. -addTemplate :: Monad m => - ByteString - -> Template - -> TemplateState m - -> TemplateState m -addTemplate n t st = insertTemplate (splitPaths n) t st - - ------------------------------------------------------------------------------- --- | Gets the node currently being processed. -getParamNode :: Monad m => TemplateMonad m Node -getParamNode = ask - - ------------------------------------------------------------------------------- --- | Stops the recursive processing of splices. -stopRecursion :: Monad m => TemplateMonad m () -stopRecursion = modify (\st -> st { _recurse = False }) - - ------------------------------------------------------------------------------- --- | Sets the current context -setContext :: Monad m => TPath -> TemplateMonad m () -setContext c = modify (\st -> st { _curContext = c }) - - ------------------------------------------------------------------------------- --- | Gets the current context -getContext :: Monad m => TemplateMonad m TPath -getContext = gets _curContext - - ------------------------------------------------------------------------------- --- | Performs splice processing on a list of nodes. -runNodeList :: Monad m => [Node] -> Splice m -runNodeList nodes = liftM concat $ sequence (map runNode nodes) - - ------------------------------------------------------------------------------- --- | Performs splice processing on a single node. -runNode :: Monad m => Node -> Splice m -runNode n@(X.Text _) = return [n] -runNode n@(X.Element nm _ ch) = do - s <- liftM (lookupSplice nm) get - maybe runChildren (recurseSplice n) s - - where - runChildren = do - newKids <- runNodeList ch - return [X.modifyChildren (const newKids) n] - - ------------------------------------------------------------------------------- --- | The maximum recursion depth. (Used to prevent infinite loops.) -mAX_RECURSION_DEPTH :: Int -mAX_RECURSION_DEPTH = 20 - - ------------------------------------------------------------------------------- --- | Checks the recursion flag and recurses accordingly. Does not recurse --- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. -recurseSplice :: Monad m => Node -> Splice m -> Splice m -recurseSplice node splice = do - result <- local (const node) splice - ts' <- get - if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH - then do modify (\st -> st { _recursionDepth = _recursionDepth st + 1 }) - res <- runNodeList result - put ts' - return res - else return result - - ------------------------------------------------------------------------------- --- | Runs a splice in the underlying monad. Splices require two --- parameters, the template state, and an input node. -runSplice :: Monad m => - TemplateState m -- ^ The initial template state - -> Node -- ^ The splice's input node - -> Splice m -- ^ The splice - -> m [Node] -runSplice ts node (TemplateMonad splice) = do - (result,_,_) <- runRWST splice node ts - return result - - ------------------------------------------------------------------------------- --- | Runs a template in the underlying monad. Similar to runSplice --- except that templates don't require a Node as a parameter. -runRawTemplate :: Monad m => TemplateState m -> Template -> m [Node] -runRawTemplate ts template = - _preRunHook ts template >>= - runSplice ts (X.Text "") . runNodeList >>= - _postRunHook ts - - ------------------------------------------------------------------------------- --- | Looks up a template name in the supplied 'TemplateState' and runs --- it in the underlying monad. -runTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe [Node]) -runTemplate ts name = - maybe (return Nothing) - (\(t,ctx) -> - return . Just =<< - runRawTemplate (ts {_curContext = ctx}) t) - (lookupTemplate name ts) - - ------------------------------------------------------------------------------- --- | Looks up a template name evaluates it. Same as runTemplate except it --- runs in TemplateMonad instead of m. -evalTemplate :: Monad m - => ByteString - -> TemplateMonad m (Maybe [Node]) -evalTemplate name = do - ts <- get - lift $ runTemplate ts name - - ------------------------------------------------------------------------------- --- | Binds a list of constant string splices -bindStrings :: Monad m - => [(ByteString, ByteString)] - -> TemplateState m - -> TemplateState m -bindStrings pairs ts = foldr add ts pairs - where - add (n,v) = bindSplice n (return [X.Text v]) - - ------------------------------------------------------------------------------- --- | Renders a template with the specified parameters. This is the function --- to use when you want to "call" a template and pass in parameters from code. -callTemplate :: Monad m - => ByteString -- ^ The name of the template - -> [(ByteString, ByteString)] -- ^ Association list of - -- (name,value) parameter pairs - -> TemplateMonad m (Maybe Template) -callTemplate name params = do - modify $ bindStrings params - evalTemplate name - - ------------------------------------------------------------------------------- --- | Renders a template from the specified TemplateState. -renderTemplate :: Monad m - => TemplateState m - -> ByteString - -> m (Maybe ByteString) -renderTemplate ts name = do - ns <- runTemplate ts name - return $ (Just . formatList') =<< ns - - ------------------------------------------------------------------------------- -heistExpatOptions :: X.ParserOptions ByteString ByteString -heistExpatOptions = - X.defaultParserOptions { - X.parserEncoding = Just X.UTF8 - , X.entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) - } - ------------------------------------------------------------------------------- --- Template loading ------------------------------------------------------------------------------- - --- | Reads an XML document from disk. -getDoc :: String -> IO (Either String Template) -getDoc f = do - bs <- catch (liftM Right $ B.readFile f) - (\(e::SomeException) -> return $ Left $ show e) - let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" - return $ (mapRight X.getChildren . - mapLeft genErrorMsg . - X.parse' heistExpatOptions . wrap) =<< bs - where - genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str - locMsg (X.XMLParseLocation line col _ _) = - "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" - translate "junk after document element" = "document must have a single root element" - translate s = s - ------------------------------------------------------------------------------- -mapLeft :: (a -> b) -> Either a c -> Either b c -mapLeft g = either (Left . g) Right -mapRight :: (b -> c) -> Either a b -> Either a c -mapRight g = either Left (Right . g) - - ------------------------------------------------------------------------------- --- | Loads a template with the specified path and filename. The --- template is only loaded if it has a ".tpl" extension. -loadTemplate :: String -- ^ path of the template root - -> String -- ^ full file path (includes the template root) - -> IO [Either String (TPath, Template)] --TemplateMap -loadTemplate templateRoot fname - | ".tpl" `isSuffixOf` fname = do - c <- getDoc fname - return [fmap (\t -> (splitPaths $ B.pack tName, t)) c] - | otherwise = return [] - where -- tName is path relative to the template root directory - tName = drop ((length templateRoot)+1) $ - -- We're only dropping the template root, not the whole path - take ((length fname) - 4) fname - - ------------------------------------------------------------------------------- --- | Traverses the specified directory structure and builds a --- TemplateState by loading all the files with a ".tpl" extension. -loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) -loadTemplates dir ts = do - d <- readDirectoryWith (loadTemplate dir) dir - let tlist = F.fold (free d) - errs = lefts tlist - case errs of - [] -> liftM Right $ foldM loadHook ts $ rights tlist - _ -> return $ Left $ unlines errs - - ------------------------------------------------------------------------------- --- | Runs the onLoad hook on the template and returns the `TemplateState` --- with the result inserted. -loadHook :: Monad m => TemplateState m -> (TPath, Template) -> IO (TemplateState m) -loadHook ts (tp, t) = do - t' <- _onLoadHook ts t - return $ insertTemplate tp t' ts - - ------------------------------------------------------------------------------- --- These are here until we can get them into hexpat. ------------------------------------------------------------------------------- - -formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> L.ByteString -formatList nodes = foldl L.append L.empty $ map formatNode nodes - -formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => - [X.Node tag text] - -> B.ByteString -formatList' = B.concat . L.toChunks . formatList - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Apply.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Apply.html deleted file mode 100644 index 83efe53..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Apply.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Apply where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - ------------------------------------------------------------------------------- --- | Default name for the apply splice. -applyTag :: ByteString -applyTag = "apply" - - ------------------------------------------------------------------------------- --- | Default attribute name for the apply tag. -applyAttr :: ByteString -applyAttr = "template" - - ------------------------------------------------------------------------------- --- | Implementation of the apply splice. -applyImpl :: Monad m => Splice m -applyImpl = do - node <- getParamNode - case X.getAttribute node applyAttr of - Nothing -> return [] -- TODO: error handling - Just attr -> do - st <- get - processedChildren <- runNodeList $ X.getChildren node - modify (bindSplice "content" $ return processedChildren) - maybe (return []) -- TODO: error handling - (\(t,ctx) -> do setContext ctx - result <- runNodeList t - put st - return result) - (lookupTemplate attr (st {_curContext = nextCtx attr st})) - where nextCtx name st - | B.isPrefixOf "/" name = [] - | otherwise = _curContext st - - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Bind.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Bind.html deleted file mode 100644 index 27c602f..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Bind.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Bind where - ------------------------------------------------------------------------------- -import Control.Monad.RWS.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Text.XML.Expat.Tree as X - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - --- | Default name for the bind splice. -bindTag :: ByteString -bindTag = "bind" - - ------------------------------------------------------------------------------- --- | Default attribute name for the bind tag. -bindAttr :: ByteString -bindAttr = "tag" - - ------------------------------------------------------------------------------- --- | Implementation of the bind splice. -bindImpl :: Monad m => Splice m -bindImpl = do - node <- getParamNode - maybe (return ()) - (add node) - (X.getAttribute node bindAttr) - return [] - - where - add node nm = modify $ bindSplice nm (return $ X.getChildren node) - - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Ignore.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Ignore.html deleted file mode 100644 index 94cfc79..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Ignore.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Ignore where - ------------------------------------------------------------------------------- -import Data.ByteString.Char8 (ByteString) - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | Default name for the ignore splice. -ignoreTag :: ByteString -ignoreTag = "ignore" - - ------------------------------------------------------------------------------- --- | The ignore tag and everything it surrounds disappears in the --- rendered output. -ignoreImpl :: Monad m => Splice m -ignoreImpl = return [] - - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Markdown.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Markdown.html deleted file mode 100644 index fc4682c..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Markdown.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} - -module Text.Templating.Heist.Splices.Markdown where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as BC -import Data.Maybe -import Control.Concurrent -import Control.Exception (throwIO) -import Control.Monad -import Control.Monad.CatchIO -import Control.Monad.Trans -import Data.Typeable -import Prelude hiding (catch) -import System.Directory -import System.Exit -import System.IO -import System.Process -import Text.Templating.Heist.Internal -import Text.XML.Expat.Tree hiding (Node) - - -data PandocMissingException = PandocMissingException - deriving (Typeable) - -instance Show PandocMissingException where - show PandocMissingException = - "Cannot find the \"pandoc\" executable; is it on your $PATH?" - -instance Exception PandocMissingException - - -data MarkdownException = MarkdownException ByteString - deriving (Typeable) - -instance Show MarkdownException where - show (MarkdownException e) = - "Markdown error: pandoc replied:\n\n" ++ BC.unpack e - -instance Exception MarkdownException - - ------------------------------------------------------------------------------- --- | Default name for the markdown splice. -markdownTag :: ByteString -markdownTag = "markdown" - ------------------------------------------------------------------------------- --- | Implementation of the markdown splice. -markdownSplice :: MonadIO m => Splice m -markdownSplice = do - pdMD <- liftIO $ findExecutable "pandoc" - - when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException - - tree <- getParamNode - markup <- liftIO $ - case getAttribute tree "file" of - Just f -> pandoc (fromJust pdMD) $ BC.unpack f - Nothing -> pandocBS (fromJust pdMD) $ textContent tree - - let ee = parse' heistExpatOptions markup - case ee of - (Left e) -> throw $ MarkdownException - $ BC.pack ("Error parsing markdown output: " ++ show e) - (Right n) -> return [n] - - -pandoc :: FilePath -> FilePath -> IO ByteString -pandoc pandocPath inputFile = do - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - - -- FIXME: hardcoded path - args = [ "-S", "--no-wrap", "templates/"++inputFile ] - - -pandocBS :: FilePath -> ByteString -> IO ByteString -pandocBS pandocPath s = do - -- using the crummy string functions for convenience here - (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s - - when (isFail ex) $ throw $ MarkdownException serr - return $ BC.concat [ "<div class=\"markdown\">\n" - , sout - , "\n</div>" ] - - where - isFail ExitSuccess = False - isFail _ = True - args = [ "-S", "--no-wrap" ] - - --- a version of readProcessWithExitCode that does I/O properly -readProcessWithExitCode' - :: FilePath -- ^ command to run - -> [String] -- ^ any arguments - -> ByteString -- ^ standard input - -> IO (ExitCode,ByteString,ByteString) -- ^ exitcode, stdout, stderr -readProcessWithExitCode' cmd args input = do - (Just inh, Just outh, Just errh, pid) <- - createProcess (proc cmd args){ std_in = CreatePipe, - std_out = CreatePipe, - std_err = CreatePipe } - outMVar <- newEmptyMVar - - outM <- newEmptyMVar - errM <- newEmptyMVar - - -- fork off a thread to start consuming stdout - forkIO $ do - out <- B.hGetContents outh - putMVar outM out - putMVar outMVar () - - -- fork off a thread to start consuming stderr - forkIO $ do - err <- B.hGetContents errh - putMVar errM err - putMVar outMVar () - - -- now write and flush any input - when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh - hClose inh -- done with stdin - - -- wait on the output - takeMVar outMVar - takeMVar outMVar - hClose outh - - -- wait on the process - ex <- waitForProcess pid - - out <- readMVar outM - err <- readMVar errM - - return (ex, out, err) - - - - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Static.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Static.html deleted file mode 100644 index 734781d..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices-Static.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings #-} - -module Text.Templating.Heist.Splices.Static - ( StaticTagState - , bindStaticTag - , clearStaticTagCache - ) where - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Monad -import Control.Monad.Trans -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B -import Data.IORef -import qualified Data.Map as Map -import Data.Map (Map) -import Data.Maybe -import qualified Data.Set as Set -import System.Random -import Text.XML.Expat.Cursor -import Text.XML.Expat.Tree hiding (Node) - - ------------------------------------------------------------------------------- -import Text.Templating.Heist.Internal - - ------------------------------------------------------------------------------- --- | State for storing static tag information -newtype StaticTagState = STS (MVar (Map ByteString Template)) - - ------------------------------------------------------------------------------- --- | Clears the static tag state. -clearStaticTagCache :: StaticTagState -> IO () -clearStaticTagCache (STS staticMVar) = - modifyMVar_ staticMVar (const $ return Map.empty) - - ------------------------------------------------------------------------------- --- | The "static" splice ensures that its contents are evaluated once and then --- cached. The cached contents are returned every time the splice is --- referenced. -staticImpl :: (MonadIO m) - => StaticTagState - -> TemplateMonad m Template -staticImpl (STS mv) = do - tree <- getParamNode - let i = fromJust $ getAttribute tree "id" - - mp <- liftIO $ readMVar mv - - (mp',ns) <- do - let mbn = Map.lookup i mp - case mbn of - Nothing -> do - nodes' <- runNodeList $ getChildren tree - return $! (Map.insert i nodes' mp, nodes') - (Just n) -> do - stopRecursion - return $! (mp,n) - - liftIO $ modifyMVar_ mv (const $ return mp') - - return ns - - ------------------------------------------------------------------------------- --- | Modifies a TemplateState to include a "static" tag. -bindStaticTag :: MonadIO m - => TemplateState m - -> IO (TemplateState m, StaticTagState) -bindStaticTag ts = do - sr <- newIORef $ Set.empty - mv <- liftM STS $ newMVar Map.empty - - return $ (addOnLoadHook (assignIds sr) $ - bindSplice "static" (staticImpl mv) ts, - mv) - - where - generateId :: IO Int - generateId = getStdRandom random - - assignIds setref = mapM f - where - f node = g $ fromTree node - - getId = do - i <- liftM (B.pack . show) generateId - st <- readIORef setref - if Set.member i st - then getId - else do - writeIORef setref $ Set.insert i st - return i - - g curs = do - let node = current curs - curs' <- if getName node == "static" - then do - i <- getId - return $ modifyContent (setAttribute "id" i) curs - else return curs - let mbc = nextDF curs' - maybe (return $ toTree curs') g mbc - - - - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices.html deleted file mode 100644 index 9919a2e..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist-Splices.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
module Text.Templating.Heist.Splices - ( module Text.Templating.Heist.Splices.Apply - , module Text.Templating.Heist.Splices.Bind - , module Text.Templating.Heist.Splices.Ignore - , module Text.Templating.Heist.Splices.Markdown - , module Text.Templating.Heist.Splices.Static - ) where - -import Text.Templating.Heist.Splices.Apply -import Text.Templating.Heist.Splices.Bind -import Text.Templating.Heist.Splices.Ignore -import Text.Templating.Heist.Splices.Markdown -import Text.Templating.Heist.Splices.Static - -- diff --git a/static/docs/0.2.4/heist/src/Text-Templating-Heist.html b/static/docs/0.2.4/heist/src/Text-Templating-Heist.html deleted file mode 100644 index d02f5a7..0000000 --- a/static/docs/0.2.4/heist/src/Text-Templating-Heist.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - -
{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} - -{-| - - This module contains the core definitions for the Heist template system. - - The Heist template system is based on XML\/xhtml. It allows you to build - custom XML-based markup languages. With Heist you can define your own - domain-specific XML tags implemented with Haskell and use them in your - templates. - - The most important concept in Heist is the 'Splice'. Splices can be thought - of as functions that transform a node into a list of nodes. Heist then - substitutes the resulting list of nodes into your template in place of the - input node. 'Splice' is implemented as a type synonym @type Splice m = - TemplateMonad m [Node]@, and 'TemplateMonad' has a function 'getParamNode' - that lets you get the input node. - - Suppose you have a place on your page where you want to display a link with - the text \"Logout username\" if the user is currently logged in or a link to - the login page if no user is logged in. Assume you have a function - @getUser :: MyAppMonad (Maybe ByteString)@ that gets the current user. - You can implement this functionality with a 'Splice' as follows: - - > - > import Text.XML.Expat.Tree - > - > link :: ByteString -> ByteString -> Node - > link target text = X.Element "a" [("href", target)] [X.Text text] - > - > loginLink :: Node - > loginLink = link "/login" "Login" - > - > logoutLink :: ByteString -> Node - > logoutLink user = link "/logout" (B.append "Logout " user) - > - > loginLogoutSplice :: Splice MyAppMonad - > loginLogoutSplice = do - > user <- lift getUser - > return $ [maybe loginLink logoutLink user] - > - - Next, you need to bind that splice to an XML tag. Heist stores information - about splices and templates in the 'TemplateState' data structure. The - following code demonstrates how this splice would be used. - - > mySplices = [ ("loginLogout", loginLogoutSplice) ] - > - > main = do - > ets <- loadTemplates "templates" $ - > foldr (uncurry bindSplice) emptyTemplateState mySplices - > let ts = either error id ets - > t <- runMyAppMonad $ renderTemplate ts "index" - > print $ maybe "Page not found" id t - - Here we build up our 'TemplateState' by starting with emptyTemplateState and - applying bindSplice for all the splices we want to add. Then we pass this - to loadTemplates our final 'TemplateState' wrapped in an Either to handle - errors. Then we use this 'TemplateState' to render our templates. - --} - -module Text.Templating.Heist - ( - -- * Types - Node - , Splice - , Template - , TemplateMonad - , TemplateState - - -- * Functions and declarations on TemplateState values - , addTemplate - , emptyTemplateState - , bindSplice - , lookupSplice - , setTemplates - , loadTemplates - - -- * Hook functions - -- $hookDoc - , addOnLoadHook - , addPreRunHook - , addPostRunHook - - -- * TemplateMonad functions - , stopRecursion - , getParamNode - , runNodeList - , getContext - - -- * Functions for running splices and templates - , runTemplate - , evalTemplate - , callTemplate - , renderTemplate - , bindStrings - - -- * Misc functions - , runSplice - , runRawTemplate - , getDoc - , bindStaticTag - - , heistExpatOptions - , module Text.Templating.Heist.Constants - ) where - -import Control.Monad.Trans -import qualified Data.Map as Map -import Text.Templating.Heist.Internal -import Text.Templating.Heist.Constants -import Text.Templating.Heist.Splices - - ------------------------------------------------------------------------------- --- | The default set of built-in splices. -defaultSpliceMap :: MonadIO m => SpliceMap m -defaultSpliceMap = Map.fromList - [(applyTag, applyImpl) - ,(bindTag, bindImpl) - ,(ignoreTag, ignoreImpl) - ,(markdownTag, markdownSplice) - ] - - ------------------------------------------------------------------------------- --- | An empty template state, with Heist's default splices (@\<bind\>@ and --- @\<apply\>@) mapped. -emptyTemplateState :: MonadIO m => TemplateState m -emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 - return return return - - --- $hookDoc --- Heist hooks allow you to modify templates when they are loaded and before --- and after they are run. Every time you call one of the addAbcHook --- functions the hook is added to onto the processing pipeline. The hooks --- processes the template in the order that they were added to the --- TemplateState. --- --- The pre-run and post-run hooks are run before and after every template is --- run/rendered. You should be careful what code you put in these hooks --- because it can significantly affect the performance of your site. - -- diff --git a/static/docs/0.2.4/heist/src/hscolour.css b/static/docs/0.2.4/heist/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.2.4/heist/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.2.4/snap-core/Data-CIByteString.html b/static/docs/0.2.4/snap-core/Data-CIByteString.html deleted file mode 100644 index 7dd5821..0000000 --- a/static/docs/0.2.4/snap-core/Data-CIByteString.html +++ /dev/null @@ -1,366 +0,0 @@ - - -
| |||||||
| |||||||
Description | |||||||
Data.CIByteString is a module containing CIByteString, a wrapper for - ByteString which provides case-insensitive (ASCII-wise) Ord and Eq - instances. - CIByteString also has an IsString instance, so if you use the - "OverloadedStrings" LANGUAGE pragma you can write case-insensitive string - literals, e.g.: - - > let a = "Foo" in - putStrLn $ (show $ unCI a) ++ "==\"FoO\" is " ++ show (a == "FoO") - "Foo"=="FoO" is True - | |||||||
Synopsis | |||||||
| |||||||
Documentation | |||||||
| |||||||
| |||||||
| |||||||
| |||||||
| |||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for (optionally) printing debugging - messages. Normally debug does nothing, but you can pass "-fdebug" to - cabal install to build a snap-core which debugs to stderr. - N.B. this is an internal interface, please don't write external code that - depends on it. - | |||||
Documentation | |||||
| |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An internal Snap module containing HTTP types. - N.B. this is an internal interface, please don't write user code that - depends on it. Most of these declarations (except for the - unsafe/encapsulation-breaking ones) are re-exported from Snap.Types. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Documentation | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets all of the values for a given header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into common log entry format. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||
| |||||
Description | |||||
An internal Snap module for debugging iteratees. - N.B. this is an internal interface, please don't write user code that - depends on it. - | |||||
Documentation | |||||
| |||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||
Snap Framework type aliases and utilities for iteratees. Note that as a - convenience, this module also exports everything from Data.Iteratee in the - iteratee library. - WARNING: Note that all of these types are scheduled to change in the - darcs head version of the iteratee library; John Lato et al. are working - on a much improved iteratee formulation. - | |||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Convenience aliases around types from Data.Iteratee - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Re-export types and functions from Data.Iteratee - | |||||||||||||||||||||||||||||||||
module Data.Iteratee | |||||||||||||||||||||||||||||||||
Helper functions - | |||||||||||||||||||||||||||||||||
Enumerators - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Enumerates a strict bytestring. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Enumerates a lazy bytestring. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Conversion to/from WrappedByteString - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Converts a wrapped bytestring to a lazy bytestring. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Converts a lazy bytestring to a wrapped bytestring. - | |||||||||||||||||||||||||||||||||
Iteratee utilities - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Reads n elements from a stream and applies the given iteratee to - the stream of the read elements. Reads exactly n elements, and if - the stream is short propagates an error. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Reads up to n elements from a stream and applies the given iteratee to the - stream of the read elements. If more than n elements are read, propagates an - error. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Wraps an Iteratee, counting the number of bytes consumed by it. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Buffers an iteratee. - Our enumerators produce a lot of little strings; rather than spending all - our time doing kernel context switches for 4-byte write() calls, we buffer - the iteratee to send 8KB at a time. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Creates a buffer to be passed into unsafeBufferIterateeWithBuffer. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Buffers an iteratee, "unsafely". Here we use a fixed binary buffer which - we'll re-use, meaning that if you hold on to any of the bytestring data - passed into your iteratee (instead of, let's say, shoving it right out a - socket) it'll get changed out from underneath you, breaking referential - transparency. Use with caution! - This version accepts a buffer created by mkIterateeBuffer. - The IORef returned can be set to True to cancel buffering. We added this - so that transfer-encoding: chunked (which needs its own buffer and therefore - doesn't need its output buffered) can switch the outer buffer off. - | |||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||
Buffers an iteratee, "unsafely". Here we use a fixed binary buffer which - we'll re-use, meaning that if you hold on to any of the bytestring data - passed into your iteratee (instead of, let's say, shoving it right out a - socket) it'll get changed out from underneath you, breaking referential - transparency. Use with caution! - The IORef returned can be set to True to cancel buffering. We added this - so that transfer-encoding: chunked (which needs its own buffer and therefore - doesn't need its output buffered) can switch the outer buffer off. - | |||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This module contains the core type definitions, class instances, and functions -for HTTP as well as the Snap monad, which is used for web handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synopsis | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Snap Monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action in the 'Iteratee IO' monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Functions for control flow and early termination - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Short-circuits a Snap monad action early, storing the given - Response value in its state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fails out of a Snap monad action. This is used to indicate - that you choose not to handle the given request within the given - handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Routing - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only if the request's HTTP method matches - the given method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap monad action only when rqPathInfo is empty. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A web handler which, given a mapping from URL entry points to web - handlers, efficiently routes requests to the correct handler. - The URL entry points are given as relative paths, for example: - route [ ("foo/bar/quux", fooBarQuux) ] - If the URI of the incoming request is - /foo/bar/quux - or - /foo/bar/quux/...anything... - then the request will be routed to "fooBarQuux", with rqContextPath - set to "/foo/bar/quux/" and rqPathInfo set to - "...anything...". - A path component within an URL entry point beginning with a colon (":") - is treated as a variable capture; the corresponding path component within - the request URI will be entered into the rqParams parameters mapping with - the given name. For instance, if the routes were: - route [ ("foo/:bar/baz", fooBazHandler) ] - Then a request for "/foo/saskatchewan/baz" would be routed to - fooBazHandler with a mapping for: - "bar" => "saskatchewan" - in its parameters table. - Longer paths are matched first, and specific routes are matched before - captures. That is, if given routes: - [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] - a request for "/a/b" will go to h2, "/a/s" for any s will go - to h3, and "/a" will go to h1. - The following example matches "/article" to an article index, - "/login" to a login, and "/article/..." to an article renderer. - route [ ("article", renderIndex) - , ("article/:id", renderArticle) - , ("login", method POST doLogin) ] - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The routeLocal function is the same as route, except it doesn't change - the request's context path. This is useful if you want to route to a - particular handler but you want that handler to receive the rqPathInfo as - it is. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Access to state - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Request object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabs the Response object out of the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Request object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Puts a new Response object into the Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the Request object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifes the Response object stored in a Snap monad. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Runs a Snap action with a locally-modified Request state - object. The Request object in the Snap monad state after the call - to localRequest will be unchanged. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Request from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Fetches the Response from state and hands it to the given action. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logging - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log an error message in the Snap monad - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Grabbing request bodies - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sends the request body through an iteratee (data consumer) and - returns the result. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the request body as a bytestring. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Detaches the request body's Enumerator from the Request and - returns it. You would want to use this if you needed to send the - HTTP request body (transformed or otherwise) through to the output - in O(1) space. (Examples: transcoding, "echo", etc) - Normally Snap is careful to ensure that the request body is fully - consumed after your web handler runs; this function is marked - "unsafe" because it breaks this guarantee and leaves the - responsibility up to you. If you don't fully consume the - Enumerator you get here, the next HTTP request in the pipeline - (if any) will misparse. Be careful with exception handlers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP Datatypes and Functions - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP-related datatypes: Request, Response, Cookie, etc. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for a case-insensitive key-value mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A type alias for the HTTP parameters mapping. Each parameter - key maps to a list of ByteString values; if a parameter is specified - multiple times (e.g.: "GET /foo?param=bar1¶m=bar2"), looking up - "param" in the mapping will give you ["bar1", "bar2"]. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Headers - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds a header key-value-pair to the HasHeaders datatype. If a header with - the same name already exists, the new value is appended to the headers list. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets a header key-value-pair in a HasHeaders datatype. If a header with - the same name already exists, it is overwritten with the new value. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gets a header value out of a HasHeaders datatype. If many headers came - in with the same name, they will be catenated together. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Requests - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The server name of the request, as it came in from the request's - Host: header. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the port number the HTTP server is listening on. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote IP address. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The remote TCP port number. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The local IP address for this request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP server's idea of its local hostname. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns True if this is an HTTPS session (currently always - False). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Content-Length of the HTTP request body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP request method. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP version used by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns a list of the cookies that came in from the HTTP request - headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Handlers can (will be; --ed) be hung on a URI "entry point"; - this is called the "context path". If a handler is hung on the - context path "/foo/", and you request "/foo/bar", the value - of rqPathInfo will be "bar". - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The "context path" of the request; catenating rqContextPath, and - rqPathInfo should get you back to the original rqURI. The - rqContextPath always begins and ends with a slash ("/") - character, and represents the path (relative to your - component/snaplet) you took to get to your handler. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the URI requested by the client. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP query string for this Request. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the Params mapping for this Request. "Parameters" are - automatically decoded from the query string and POST body and - entered into this mapping. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies the parameters mapping (which is a Map ByteString ByteString) in - a Request using the given function. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Responses - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
An empty Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status code. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Returns the HTTP status explanation string. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the Content-Type in the Response headers. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A note here: if you want to set the Content-Length for the response, - Snap forces you to do it with this function rather than by setting it in the - headers; the Content-Length in the headers will be ignored. - The reason for this is that Snap needs to look up the value of - Content-Length for each request, and looking the string value up in the - headers and parsing the number out of the text will be too expensive. - If you don't set a content length in your response, HTTP keep-alive will be - disabled for HTTP/1.0 clients, forcing a Connection: close. For HTTP/1.1 - clients, Snap will switch to the chunked transfer encoding if - Content-Length is not specified. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Removes any Content-Length set in the Response. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Response I/O - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modifies a response body. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given strict Text to the body of the Response stored in the - Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Adds the given lazy ByteString to the body of the Response stored in - the Snap monad state. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sets the output to be the contents of the specified file. - Calling sendFile will overwrite any output queued to be sent in the - Response. If the response body is not modified after the call to - sendFile, Snap will use the efficient sendfile() system call on - platforms that support it. - If the response body is modified (using modifyResponseBody), the file will - be read using mmap(). - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Iteratee - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTTP utilities - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts a CTime into an HTTP timestamp. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Converts an HTTP timestamp into a CTime. - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URL-escapes a string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Decodes an URL-escaped string (see - http://tools.ietf.org/html/rfc2396.html#section-2.4) - | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||
| |||||||||||||
Description | |||||||||||||
Contains web handlers to serve files from a directory. - | |||||||||||||
Synopsis | |||||||||||||
| |||||||||||||
Documentation | |||||||||||||
| |||||||||||||
Gets a path from the Request using rqPathInfo and makes sure it is - safe to use for opening files. A path is safe if it is a relative path - and has no .. elements to escape the intended directory structure. - | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
The default set of mime type mappings we use when serving files. Its - value: - Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - | |||||||||||||
| |||||||||||||
A type alias for MIME type - | |||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||
| ||||||||
Synopsis | ||||||||
| ||||||||
Documentation | ||||||||
| ||||||||
| ||||||||
| ||||||||
| ||||||||
Produced by Haddock version 2.6.1 |
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (>) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (A) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (B) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||
Index (C) | ||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (D) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
Index (E) | ||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
Index (F) | ||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (G) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (H) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (I) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (J) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (L) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (M) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (N) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (O) | |||||||||||||||||||||
|
| ||||||||||||||||||||||||
| ||||||||||||||||||||||||
Index (P) | ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Index (R) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||
Index (S) | ||||||||||||||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (T) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (U) | |||||||||||||||||||||
|
| |||||||||||||||||||||
| |||||||||||||||||||||
Index (W) | |||||||||||||||||||||
|
| |||||||||||||||||||||
Index | |||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
snap-core-0.2.4: Snap: A Haskell Web Framework (Core) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - This library contains the core definitions and types for the Snap framework, -including: - 1. Primitive types and functions for HTTP (requests, responses, cookies, -post/query parameters, etc) - 2. Type aliases and helper functions for Iteratee I/O - 3. A monad for programming web handlers called "Snap", inspired by -happstack's (http://happstack.com/index.html), which allows: -
Quick start: The Snap monad and HTTP definitions are in Snap.Types, -some iteratee utilities are in Snap.Iteratee. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - --- | "Data.CIByteString" is a module containing 'CIByteString', a wrapper for --- 'ByteString' which provides case-insensitive (ASCII-wise) 'Ord' and 'Eq' --- instances. --- --- 'CIByteString' also has an 'IsString' instance, so if you use the --- \"OverloadedStrings\" LANGUAGE pragma you can write case-insensitive string --- literals, e.g.: --- --- @ --- \> let a = \"Foo\" in --- putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++ show (a == \"FoO\") --- \"Foo\"==\"FoO\" is True --- @ - -module Data.CIByteString - ( CIByteString - , toCI - , unCI - , ciToLower - ) where - --- for IsString instance -import Data.ByteString.Char8 () -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString as S -import Data.Char -import Data.String - - --- | A case-insensitive newtype wrapper for 'ByteString' -data CIByteString = CIByteString { unCI :: !ByteString - , _lowercased :: !ByteString } - -toCI :: ByteString -> CIByteString -toCI s = CIByteString s t - where - t = lowercase s - -ciToLower :: CIByteString -> ByteString -ciToLower = _lowercased - -instance Show CIByteString where - show (CIByteString s _) = show s - -lowercase :: ByteString -> ByteString -lowercase = S.map (c2w . toLower . w2c) - -instance Eq CIByteString where - (CIByteString _ a) == (CIByteString _ b) = a == b - (CIByteString _ a) /= (CIByteString _ b) = a /= b - -instance Ord CIByteString where - (CIByteString _ a) <= (CIByteString _ b) = a <= b - -instance IsString CIByteString where - fromString = toCI . fromString -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Internal-Debug.html b/static/docs/0.2.4/snap-core/src/Snap-Internal-Debug.html deleted file mode 100644 index aafa21f..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Internal-Debug.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - -
-- | An internal Snap module for (optionally) printing debugging --- messages. Normally 'debug' does nothing, but you can pass \"-fdebug\" to --- @cabal install@ to build a @snap-core@ which debugs to stderr. --- --- /N.B./ this is an internal interface, please don't write external code that --- depends on it. - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} - -module Snap.Internal.Debug where - -import Control.Monad.Trans - -#ifdef DEBUG_TEST - -debug :: (MonadIO m) => String -> m () -debug !s = return $ s `seq` () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno !s = return $ s `seq` () - -#elif defined(DEBUG) - ------------------------------------------------------------------------------- -import Control.Concurrent -import Data.List -import Data.Maybe -import Foreign.C.Error -import System.IO -import System.IO.Unsafe -import Text.Printf ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -_debugMVar :: MVar () -_debugMVar = unsafePerformIO $ newMVar () -{-# NOINLINE _debugMVar #-} - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug s = liftIO $ withMVar _debugMVar $ \_ -> do - tid <- myThreadId - hPutStrLn stderr $ s' tid - hFlush stderr - where - chop x = let y = fromMaybe x $ stripPrefix "ThreadId " x - in printf "%8s" y - - s' t = "[" ++ chop (show t) ++ "] " ++ s - -{-# INLINE debug #-} - - ------------------------------------------------------------------------------- -debugErrno :: (MonadIO m) => String -> m () -debugErrno loc = liftIO $ do - err <- getErrno - let ex = errnoToIOError loc err Nothing Nothing - debug $ show ex ------------------------------------------------------------------------------- - -#else - ------------------------------------------------------------------------------- -debug :: (MonadIO m) => String -> m () -debug _ = return () -{-# INLINE debug #-} - -debugErrno :: (MonadIO m) => String -> m () -debugErrno _ = return () ------------------------------------------------------------------------------- - -#endif -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Internal-Http-Types.html b/static/docs/0.2.4/snap-core/src/Snap-Internal-Http-Types.html deleted file mode 100644 index 32777ab..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Internal-Http-Types.html +++ /dev/null @@ -1,693 +0,0 @@ - - - - -
-- | An internal Snap module containing HTTP types. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. Most of these declarations (except for the --- unsafe/encapsulation-breaking ones) are re-exported from "Snap.Types". - -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeSynonymInstances #-} - -module Snap.Internal.Http.Types where - - ------------------------------------------------------------------------------- -import Control.Applicative hiding (empty) -import Control.Monad (liftM, when) -import qualified Data.Attoparsec as Atto -import Data.Attoparsec hiding (many, Result(..)) -import Data.Bits -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w,w2c) -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import qualified Data.ByteString as S -import qualified Data.ByteString.Unsafe as S -import Data.Char -import Data.DList (DList) -import qualified Data.DList as DL -import Data.IORef -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Monoid -import Data.Serialize.Builder -import Data.Time.Clock -import Data.Time.Format -import Data.Word -import Foreign hiding (new) -import Foreign.C.Types -import Prelude hiding (take) -import System.Locale (defaultTimeLocale) - - -#ifdef PORTABLE -import Data.Time.LocalTime -import Data.Time.Clock.POSIX -#else -import Foreign.C.String -#endif - ------------------------------------------------------------------------------- -import Data.CIByteString -import qualified Snap.Iteratee as I - - -#ifndef PORTABLE - ------------------------------------------------------------------------------- --- foreign imports from cbits - -foreign import ccall unsafe "set_c_locale" - set_c_locale :: IO () - -foreign import ccall unsafe "c_parse_http_time" - c_parse_http_time :: CString -> IO CTime - -foreign import ccall unsafe "c_format_http_time" - c_format_http_time :: CTime -> CString -> IO () - -foreign import ccall unsafe "c_format_log_time" - c_format_log_time :: CTime -> CString -> IO () - -#endif - ------------------------------------------------------------------------------- -type Enumerator a = I.Enumerator IO a - ------------------------------------------------------------------------------- --- | A type alias for a case-insensitive key-value mapping. -type Headers = Map CIByteString [ByteString] - - ------------------------------------------------------------------------------- --- | A typeclass for datatypes which contain HTTP headers. -class HasHeaders a where - - -- | Modify the datatype's headers. - updateHeaders :: (Headers -> Headers) -> a -> a - - -- | Retrieve the headers from a datatype that has headers. - headers :: a -> Headers - - ------------------------------------------------------------------------------- --- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header with --- the same name already exists, the new value is appended to the headers list. -addHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -addHeader k v = updateHeaders $ Map.insertWith' (++) k [v] - - ------------------------------------------------------------------------------- --- | Sets a header key-value-pair in a 'HasHeaders' datatype. If a header with --- the same name already exists, it is overwritten with the new value. -setHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a -setHeader k v = updateHeaders $ Map.insert k [v] - - ------------------------------------------------------------------------------- --- | Gets all of the values for a given header. -getHeaders :: (HasHeaders a) => CIByteString -> a -> Maybe [ByteString] -getHeaders k a = Map.lookup k $ headers a - - ------------------------------------------------------------------------------- --- | Gets a header value out of a 'HasHeaders' datatype. If many headers came --- in with the same name, they will be catenated together. -getHeader :: (HasHeaders a) => CIByteString -> a -> Maybe ByteString -getHeader k a = liftM (S.intercalate " ") (Map.lookup k $ headers a) - - ------------------------------------------------------------------------------- --- | Enumerates the HTTP method values (see --- <http://tools.ietf.org/html/rfc2068.html#section-5.1.1>). -data Method = GET | HEAD | POST | PUT | DELETE | TRACE | OPTIONS | CONNECT - deriving(Show,Read,Ord,Eq) - - ------------------------------------------------------------------------------- -type HttpVersion = (Int,Int) - - ------------------------------------------------------------------------------- --- | A datatype representing an HTTP cookie. -data Cookie = Cookie { - -- | The name of the cookie. - cookieName :: !ByteString - - -- | The cookie's string value. - , cookieValue :: !ByteString - - -- | The cookie's expiration value, if it has one. - , cookieExpires :: !(Maybe UTCTime) - - -- | The cookie's \"domain\" value, if it has one. - , cookieDomain :: !(Maybe ByteString) - - -- | The cookie path. - , cookiePath :: !(Maybe ByteString) -} deriving (Eq, Show) - - ------------------------------------------------------------------------------- --- | A type alias for the HTTP parameters mapping. Each parameter --- key maps to a list of ByteString values; if a parameter is specified --- multiple times (e.g.: \"@GET /foo?param=bar1¶m=bar2@\"), looking up --- \"@param@\" in the mapping will give you @[\"bar1\", \"bar2\"]@. -type Params = Map ByteString [ByteString] - - ------------------------------------------------------------------------------- --- request type ------------------------------------------------------------------------------- - -data SomeEnumerator = SomeEnumerator (forall a . Enumerator a) - - ------------------------------------------------------------------------------- --- | Contains all of the information about an incoming HTTP request. -data Request = Request - { -- | The server name of the request, as it came in from the request's - -- @Host:@ header. - rqServerName :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqServerPort :: !Int - - -- | The remote IP address. - , rqRemoteAddr :: !ByteString - - -- | The remote TCP port number. - , rqRemotePort :: !Int - - -- | The local IP address for this request. - , rqLocalAddr :: !ByteString - - -- | Returns the port number the HTTP server is listening on. - , rqLocalPort :: !Int - - -- | Returns the HTTP server's idea of its local hostname. - , rqLocalHostname :: !ByteString - - -- | Returns @True@ if this is an @HTTPS@ session (currently always - -- @False@). - , rqIsSecure :: !Bool - , rqHeaders :: Headers - , rqBody :: IORef SomeEnumerator - - -- | Returns the @Content-Length@ of the HTTP request body. - , rqContentLength :: !(Maybe Int) - - -- | Returns the HTTP request method. - , rqMethod :: !Method - - -- | Returns the HTTP version used by the client. - , rqVersion :: !HttpVersion - - -- | Returns a list of the cookies that came in from the HTTP request - -- headers. - , rqCookies :: [Cookie] - - - -- | We'll be doing web components (or \"snaplets\") for version 0.2. The - -- \"snaplet path\" refers to the place on the URL where your containing - -- snaplet is hung. The value of 'rqSnapletPath' is either @\"\"@ (at the - -- top-level context) or is a path beginning with a slash, but not ending - -- with one. - -- - -- An identity is that: - -- - -- > rqURI r == 'S.concat' [ rqSnapletPath r - -- > , rqContextPath r - -- > , rqPathInfo r ] - -- - -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will be - -- \"\" - , rqSnapletPath :: !ByteString - - -- | Handlers can (/will be; --ed/) be hung on a @URI@ \"entry point\"; - -- this is called the \"context path\". If a handler is hung on the - -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the value - -- of 'rqPathInfo' will be @\"bar\"@. - , rqPathInfo :: !ByteString - - -- | The \"context path\" of the request; catenating 'rqContextPath', and - -- 'rqPathInfo' should get you back to the original 'rqURI'. The - -- 'rqContextPath' always begins and ends with a slash (@\"\/\"@) - -- character, and represents the path (relative to your - -- component\/snaplet) you took to get to your handler. - , rqContextPath :: !ByteString - - -- | Returns the @URI@ requested by the client. - , rqURI :: !ByteString - - -- | Returns the HTTP query string for this 'Request'. - , rqQueryString :: !ByteString - - -- | Returns the 'Params' mapping for this 'Request'. \"Parameters\" are - -- automatically decoded from the query string and @POST@ body and - -- entered into this mapping. - , rqParams :: Params - } - - ------------------------------------------------------------------------------- -instance Show Request where - show r = concat [ "Request <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - sname - , remote - , local - , beginheaders - , hdrs - , endheaders - , contentlength - , method - , version - , cookies - , pathinfo - , contextpath - , snapletpath - , uri - , params - ] - - sname = concat [ "server-name: ", toStr $ rqServerName r ] - remote = concat [ "remote: " - , toStr $ rqRemoteAddr r - , ":" - , show (rqRemotePort r) - ] - local = concat [ "local: " - , toStr $ rqLocalAddr r - , ":" - , show $ rqServerPort r - ] - beginheaders = "Headers:\n ========================================" - endheaders = " ========================================" - hdrs = " " ++ show (rqHeaders r) - contentlength = concat [ "content-length: " - , show $ rqContentLength r - ] - method = concat [ "method: " - , show $ rqMethod r - ] - version = concat [ "version: " - , show $ rqVersion r - ] - cookies = concat [ "cookies:\n" - , " ========================================\n" - , " " ++ (show $ rqCookies r) - , "\n ========================================" - ] - pathinfo = concat [ "pathinfo: ", toStr $ rqPathInfo r ] - contextpath = concat [ "contextpath: ", toStr $ rqContextPath r ] - snapletpath = concat [ "snapletpath: ", toStr $ rqSnapletPath r ] - uri = concat [ "URI: ", toStr $ rqURI r ] - params = concat [ "params:\n" - , " ========================================\n" - , " " ++ (show $ rqParams r) - , "\n ========================================" - ] - - ------------------------------------------------------------------------------- -instance HasHeaders Request where - headers = rqHeaders - updateHeaders f r = r { rqHeaders = f (rqHeaders r) } - - ------------------------------------------------------------------------------- -instance HasHeaders Headers where - headers = id - updateHeaders = id - ------------------------------------------------------------------------------- --- response type ------------------------------------------------------------------------------- - -data ResponseBody = Enum (forall a . Enumerator a) -- ^ output body is enumerator - | SendFile FilePath -- ^ output body is sendfile() - - ------------------------------------------------------------------------------- -rspBodyMap :: (forall a . Enumerator a -> Enumerator a) - -> ResponseBody - -> ResponseBody -rspBodyMap f b = Enum $ f $ rspBodyToEnum b - - ------------------------------------------------------------------------------- -rspBodyToEnum :: ResponseBody -> Enumerator a -rspBodyToEnum (Enum e) = e -rspBodyToEnum (SendFile fp) = I.enumFile fp - - ------------------------------------------------------------------------------- --- | Represents an HTTP response. -data Response = Response - { rspHeaders :: Headers - , rspHttpVersion :: !HttpVersion - - -- | We will need to inspect the content length no matter what, and - -- looking up \"content-length\" in the headers and parsing the number - -- out of the text will be too expensive. - , rspContentLength :: !(Maybe Int) - , rspBody :: ResponseBody - - -- | Returns the HTTP status code. - , rspStatus :: !Int - - -- | Returns the HTTP status explanation string. - , rspStatusReason :: !ByteString - } - - ------------------------------------------------------------------------------- -instance Show Response where - show r = concat [ "Response <\n" - , body - , ">" ] - where - body = concat $ map ((" "++) . (++ "\n")) [ - hdrs - , version - , status - , reason - ] - - hdrs = concat [ "headers:\n" - , " ==============================\n " - , show $ rspHeaders r - , "\n ==============================" ] - - version = concat [ "version: ", show $ rspHttpVersion r ] - status = concat [ "status: ", show $ rspStatus r ] - reason = concat [ "reason: ", toStr $ rspStatusReason r ] - - ------------------------------------------------------------------------------- -instance HasHeaders Response where - headers = rspHeaders - updateHeaders f r = r { rspHeaders = f (rspHeaders r) } - - ------------------------------------------------------------------------------- --- | Looks up the value(s) for the given named parameter. Parameters initially --- come from the request's query string and any decoded POST body (if the --- request's @Content-Type@ is @application\/x-www-form-urlencoded@). Parameter --- values can be modified within handlers using "rqModifyParams". -rqParam :: ByteString -- ^ parameter name to look up - -> Request -- ^ HTTP request - -> Maybe [ByteString] -rqParam k rq = Map.lookup k $ rqParams rq -{-# INLINE rqParam #-} - - ------------------------------------------------------------------------------- --- | Modifies the parameters mapping (which is a @Map ByteString ByteString@) in --- a 'Request' using the given function. -rqModifyParams :: (Params -> Params) -> Request -> Request -rqModifyParams f r = r { rqParams = p } - where - p = f $ rqParams r -{-# INLINE rqModifyParams #-} - - ------------------------------------------------------------------------------- --- | Writes a key-value pair to the parameters mapping within the given request. -rqSetParam :: ByteString -- ^ parameter name - -> [ByteString] -- ^ parameter values - -> Request -- ^ request - -> Request -rqSetParam k v = rqModifyParams $ Map.insert k v -{-# INLINE rqSetParam #-} - ------------------------------------------------------------------------------- --- responses ------------------------------------------------------------------------------- - --- | An empty 'Response'. -emptyResponse :: Response -emptyResponse = Response Map.empty (1,1) Nothing (Enum return) 200 "OK" - - ------------------------------------------------------------------------------- --- | Sets an HTTP response body to the given 'Enumerator' value. -setResponseBody :: (forall a . Enumerator a) -- ^ new response body - -- enumerator - -> Response -- ^ response to modify - -> Response -setResponseBody e r = r { rspBody = Enum e } -{-# INLINE setResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the HTTP response status. -setResponseStatus :: Int -- ^ HTTP response integer code - -> ByteString -- ^ HTTP response explanation - -> Response -- ^ Response to be modified - -> Response -setResponseStatus s reason r = r { rspStatus=s, rspStatusReason=reason } -{-# INLINE setResponseStatus #-} - - ------------------------------------------------------------------------------- --- | Modifies a response body. -modifyResponseBody :: (forall a . Enumerator a -> Enumerator a) - -> Response - -> Response -modifyResponseBody f r = r { rspBody = rspBodyMap f (rspBody r) } -{-# INLINE modifyResponseBody #-} - - ------------------------------------------------------------------------------- --- | Sets the @Content-Type@ in the 'Response' headers. -setContentType :: ByteString -> Response -> Response -setContentType = setHeader "Content-Type" -{-# INLINE setContentType #-} - - ------------------------------------------------------------------------------- --- | Adds an HTTP 'Cookie' to the 'Response' headers. -addCookie :: Cookie -- ^ cookie value - -> Response -- ^ response to modify - -> Response -addCookie (Cookie k v mbExpTime mbDomain mbPath) = updateHeaders f - where - f = Map.insertWith' (++) "Set-Cookie" [cookie] - cookie = S.concat [k, "=", v, path, exptime, domain] - path = maybe "" (S.append "; path=") mbPath - domain = maybe "" (S.append "; domain=") mbDomain - exptime = maybe "" (S.append "; expires=" . fmt) mbExpTime - fmt = fromStr . formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT" - - ------------------------------------------------------------------------------- --- | A note here: if you want to set the @Content-Length@ for the response, --- Snap forces you to do it with this function rather than by setting it in the --- headers; the @Content-Length@ in the headers will be ignored. --- --- The reason for this is that Snap needs to look up the value of --- @Content-Length@ for each request, and looking the string value up in the --- headers and parsing the number out of the text will be too expensive. --- --- If you don't set a content length in your response, HTTP keep-alive will be --- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For HTTP\/1.1 --- clients, Snap will switch to the chunked transfer encoding if --- @Content-Length@ is not specified. -setContentLength :: Int -> Response -> Response -setContentLength l r = r { rspContentLength = Just l } -{-# INLINE setContentLength #-} - - ------------------------------------------------------------------------------- --- | Removes any @Content-Length@ set in the 'Response'. -clearContentLength :: Response -> Response -clearContentLength r = r { rspContentLength = Nothing } -{-# INLINE clearContentLength #-} - - ------------------------------------------------------------------------------- --- HTTP dates - --- | Converts a 'CTime' into an HTTP timestamp. -formatHttpTime :: CTime -> IO ByteString - --- | Converts a 'CTime' into common log entry format. -formatLogTime :: CTime -> IO ByteString - --- | Converts an HTTP timestamp into a 'CTime'. -parseHttpTime :: ByteString -> IO CTime - -#ifdef PORTABLE - -formatHttpTime = return . format . toUTCTime - where - format :: UTCTime -> ByteString - format = fromStr . formatTime defaultTimeLocale "%a, %d %b %Y %X GMT" - - toUTCTime :: CTime -> UTCTime - toUTCTime = posixSecondsToUTCTime . realToFrac - -formatLogTime ctime = do - t <- utcToLocalZonedTime $ toUTCTime ctime - return $ format t - - where - format :: ZonedTime -> ByteString - format = fromStr . formatTime defaultTimeLocale "%d/%b/%Y:%H:%M:%S %z" - - toUTCTime :: CTime -> UTCTime - toUTCTime = posixSecondsToUTCTime . realToFrac - - -parseHttpTime = return . toCTime . parse . toStr - where - parse :: String -> Maybe UTCTime - parse = parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" - - toCTime :: Maybe UTCTime -> CTime - toCTime (Just t) = fromInteger $ truncate $ utcTimeToPOSIXSeconds t - toCTime Nothing = fromInteger 0 - -#else - -formatLogTime t = do - ptr <- mallocBytes 40 - c_format_log_time t ptr - S.unsafePackMallocCString ptr - -formatHttpTime t = do - ptr <- mallocBytes 40 - c_format_http_time t ptr - S.unsafePackMallocCString ptr - -parseHttpTime s = S.unsafeUseAsCString s $ \ptr -> - c_parse_http_time ptr - -#endif - - ------------------------------------------------------------------------------- --- URL ENCODING ------------------------------------------------------------------------------- - -parseToCompletion :: Parser a -> ByteString -> Maybe a -parseToCompletion p s = toResult $ finish r - where - r = parse p s - - toResult (Atto.Done _ c) = Just c - toResult _ = Nothing - - ------------------------------------------------------------------------------- -pUrlEscaped :: Parser ByteString -pUrlEscaped = do - sq <- nextChunk DL.empty - return $ S.concat $ DL.toList sq - - where - nextChunk :: DList ByteString -> Parser (DList ByteString) - nextChunk s = (endOfInput *> pure s) <|> do - c <- anyWord8 - case w2c c of - '+' -> plusSpace s - '%' -> percentEncoded s - _ -> unEncoded c s - - percentEncoded :: DList ByteString -> Parser (DList ByteString) - percentEncoded l = do - hx <- take 2 - when (S.length hx /= 2 || - (not $ S.all (isHexDigit . w2c) hx)) $ - fail "bad hex in url" - - let code = (Cvt.hex hx) :: Word8 - nextChunk $ DL.snoc l (S.singleton code) - - unEncoded :: Word8 -> DList ByteString -> Parser (DList ByteString) - unEncoded c l' = do - let l = DL.snoc l' (S.singleton c) - bs <- takeTill (flip elem (map c2w "%+")) - if S.null bs - then nextChunk l - else nextChunk $ DL.snoc l bs - - plusSpace :: DList ByteString -> Parser (DList ByteString) - plusSpace l = nextChunk (DL.snoc l (S.singleton $ c2w ' ')) - - ------------------------------------------------------------------------------- --- | Decodes an URL-escaped string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlDecode :: ByteString -> Maybe ByteString -urlDecode = parseToCompletion pUrlEscaped - - ------------------------------------------------------------------------------- --- "...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," --- [not including the quotes - ed], and reserved characters used for their --- reserved purposes may be used unencoded within a URL." - --- | URL-escapes a string (see --- <http://tools.ietf.org/html/rfc2396.html#section-2.4>) -urlEncode :: ByteString -> ByteString -urlEncode = toByteString . S.foldl' f empty - where - f b c = - if c == c2w ' ' - then b `mappend` singleton (c2w '+') - else if isKosher c - then b `mappend` singleton c - else b `mappend` hexd c - - isKosher w = any ($ c) [ isAlphaNum - , flip elem ['$', '-', '.', '!', '*' - , '\'', '(', ')', ',' ]] - where - c = w2c w - - ------------------------------------------------------------------------------- -hexd :: Word8 -> Builder -hexd c = singleton (c2w '%') `mappend` singleton hi `mappend` singleton low - where - d = c2w . intToDigit - low = d $ fromEnum $ c .&. 0xf - hi = d $ fromEnum $ (c .&. 0xf0) `shift` (-4) - - ------------------------------------------------------------------------------- -finish :: Atto.Result a -> Atto.Result a -finish (Atto.Partial f) = flip feed "" $ f "" -finish x = x - - ------------------------------------------------------------------------------- --- local definitions -fromStr :: String -> ByteString -fromStr = S.pack . map c2w -{-# INLINE fromStr #-} - ------------------------------------------------------------------------------- --- private helper functions -toStr :: ByteString -> String -toStr = map w2c . S.unpack - -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Internal-Iteratee-Debug.html b/static/docs/0.2.4/snap-core/src/Snap-Internal-Iteratee-Debug.html deleted file mode 100644 index 972fc9d..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Internal-Iteratee-Debug.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - -
-- | An internal Snap module for debugging iteratees. --- --- /N.B./ this is an internal interface, please don't write user code that --- depends on it. - -{-# OPTIONS_GHC -fno-warn-orphans #-} -{-# LANGUAGE FlexibleInstances #-} - -module Snap.Internal.Iteratee.Debug ( debugIteratee ) where - ------------------------------------------------------------------------------- -import Data.Iteratee.WrappedByteString -import Data.Word (Word8) -import System.IO ------------------------------------------------------------------------------- -import Snap.Iteratee ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -instance Show (WrappedByteString Word8) where - show (WrapBS s) = show s - - ------------------------------------------------------------------------------- -debugIteratee :: Iteratee IO () -debugIteratee = IterateeG f - where - f c@(EOF _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return (Done () c) - - f c@(Chunk _) = do - putStrLn $ "got chunk: " ++ show c - hFlush stdout - return $ Cont debugIteratee Nothing -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Internal-Routing.html b/static/docs/0.2.4/snap-core/src/Snap-Internal-Routing.html deleted file mode 100644 index 63cffe2..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Internal-Routing.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - -
module Snap.Internal.Routing where - - ------------------------------------------------------------------------------- -import Control.Applicative ((<|>)) -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w) -import qualified Data.ByteString as B -import Data.Monoid -import qualified Data.Map as Map - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Types - - ------------------------------------------------------------------------------- -{-| - -The internal data type you use to build a routing tree. Matching is -done unambiguously. - -'Capture' and 'Dir' routes can have a "fallback" route: - - - For 'Capture', the fallback is routed when there is nothing to capture - - For 'Dir', the fallback is routed when we can't find a route in its map - -Fallback routes are stacked: i.e. for a route like: - -> Dir [("foo", Capture "bar" (Action bar) NoRoute)] baz - -visiting the URI foo/ will result in the "bar" capture being empty and -triggering its fallback. It's NoRoute, so we go to the nearest parent -fallback and try that, which is the baz action. - --} -data Route a = Action (Snap a) -- wraps a 'Snap' action - | Capture ByteString (Route a) (Route a) -- captures the dir in a param - | Dir (Map.Map ByteString (Route a)) (Route a) -- match on a dir - | NoRoute - - ------------------------------------------------------------------------------- -instance Monoid (Route a) where - mempty = NoRoute - - -- Unions two routes, favoring the right-hand side - mappend NoRoute r = r - - mappend l@(Action _) r = case r of - (Action _) -> r - (Capture p r' fb) -> Capture p r' (mappend fb l) - (Dir _ _) -> mappend (Dir Map.empty l) r - NoRoute -> l - - mappend l@(Capture p r' fb) r = case r of - (Action _) -> Capture p r' (mappend fb r) - (Capture p' r'' fb') - | p == p' -> Capture p (mappend r' r'') (mappend fb fb') - | otherwise -> r - (Dir rm fb') -> Dir rm (mappend fb' l) - NoRoute -> l - - mappend l@(Dir rm fb) r = case r of - (Action _) -> Dir rm (mappend fb r) - (Capture _ _ _) -> Dir rm (mappend fb r) - (Dir rm' fb') -> Dir (Map.unionWith mappend rm rm') (mappend fb fb') - NoRoute -> l - - ------------------------------------------------------------------------------- --- | A web handler which, given a mapping from URL entry points to web --- handlers, efficiently routes requests to the correct handler. --- --- The URL entry points are given as relative paths, for example: --- --- > route [ ("foo/bar/quux", fooBarQuux) ] --- --- If the URI of the incoming request is --- --- > /foo/bar/quux --- --- or --- --- > /foo/bar/quux/...anything... --- --- then the request will be routed to \"@fooBarQuux@\", with 'rqContextPath' --- set to \"@\/foo\/bar\/quux\/@\" and 'rqPathInfo' set to --- \"@...anything...@\". --- --- A path component within an URL entry point beginning with a colon (\"@:@\") --- is treated as a /variable capture/; the corresponding path component within --- the request URI will be entered into the 'rqParams' parameters mapping with --- the given name. For instance, if the routes were: --- --- > route [ ("foo/:bar/baz", fooBazHandler) ] --- --- Then a request for \"@\/foo\/saskatchewan\/baz@\" would be routed to --- @fooBazHandler@ with a mapping for: --- --- > "bar" => "saskatchewan" --- --- in its parameters table. --- --- Longer paths are matched first, and specific routes are matched before --- captures. That is, if given routes: --- --- > [ ("a", h1), ("a/b", h2), ("a/:x", h3) ] --- --- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will go --- to @h3@, and \"@\/a@\" will go to @h1@. --- --- The following example matches \"@\/article@\" to an article index, --- \"@\/login@\" to a login, and \"@\/article\/...@\" to an article renderer. --- --- > route [ ("article", renderIndex) --- > , ("article/:id", renderArticle) --- > , ("login", method POST doLogin) ] --- -route :: [(ByteString, Snap a)] -> Snap a -route rts = route' (return ()) rts' [] - where - rts' = mconcat (map pRoute rts) - - ------------------------------------------------------------------------------- --- | The 'routeLocal' function is the same as 'route', except it doesn't change --- the request's context path. This is useful if you want to route to a --- particular handler but you want that handler to receive the 'rqPathInfo' as --- it is. -routeLocal :: [(ByteString, Snap a)] -> Snap a -routeLocal rts' = do - req <- getRequest - let ctx = rqContextPath req - let p = rqPathInfo req - let md = modifyRequest $ \r -> r {rqContextPath=ctx, rqPathInfo=p} - - route' md rts [] <|> (md >> pass) - - where - rts = mconcat (map pRoute rts') - - ------------------------------------------------------------------------------- -pRoute :: (ByteString, Snap a) -> Route a -pRoute (r, a) = foldr f (Action a) hier - where - hier = filter (not . B.null) $ B.splitWith (== (c2w '/')) r - f s rt = if B.head s == c2w ':' - then Capture (B.tail s) rt NoRoute - else Dir (Map.fromList [(s, rt)]) NoRoute - - ------------------------------------------------------------------------------- -route' :: Snap () -- ^ an action to be run before any user - -- handler - -> Route a -- ^ currently active routing table - -> [Route a] -- ^ list of fallback routing tables in case - -- the current table fails - -> Snap a -route' pre (Action action) _ = pre >> action - -route' pre (Capture param rt fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - if B.null cwd - then route' pre fb fbs - else do localRequest (updateContextPath (B.length cwd) . (f cwd)) $ - route' pre rt (fb:fbs) - where - f v req = req { rqParams = Map.insertWith (++) param [v] (rqParams req) } - -route' pre (Dir rtm fb) fbs = do - cwd <- getRequest >>= return . B.takeWhile (/= (c2w '/')) . rqPathInfo - case Map.lookup cwd rtm of - Just rt -> do - localRequest (updateContextPath (B.length cwd)) $ - route' pre rt (fb:fbs) - Nothing -> route' pre fb fbs - -route' _ NoRoute [] = pass -route' pre NoRoute (fb:fbs) = route' pre fb fbs -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Internal-Types.html b/static/docs/0.2.4/snap-core/src/Snap-Internal-Types.html deleted file mode 100644 index 1aa853c..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Internal-Types.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Types where - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Exception (throwIO, ErrorCall(..)) -import Control.Monad.CatchIO -import Control.Monad.State.Strict -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.IORef -import qualified Data.Iteratee as Iter -import Data.Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Data.Text.Lazy as LT -import qualified Data.Text.Lazy.Encoding as LT - -import Data.Typeable - ------------------------------------------------------------------------------- -import Snap.Iteratee hiding (Enumerator) -import Snap.Internal.Http.Types - - ------------------------------------------------------------------------------- --- The Snap Monad ------------------------------------------------------------------------------- - -{-| - -'Snap' is the 'Monad' that user web handlers run in. 'Snap' gives you: - -1. stateful access to fetch or modify an HTTP 'Request' - -2. stateful access to fetch or modify an HTTP 'Response' - -3. failure \/ 'Alternative' \/ 'MonadPlus' semantics: a 'Snap' handler can - choose not to handle a given request, using 'empty' or its synonym 'pass', - and you can try alternative handlers with the '<|>' operator: - - > a :: Snap String - > a = pass - > - > b :: Snap String - > b = return "foo" - > - > c :: Snap String - > c = a <|> b -- try running a, if it fails then try b - -4. convenience functions ('writeBS', 'writeLBS', 'writeText', 'writeLazyText', - 'addToOutput') for writing output to the 'Response': - - > a :: (forall a . Enumerator a) -> Snap () - > a someEnumerator = do - > writeBS "I'm a strict bytestring" - > writeLBS "I'm a lazy bytestring" - > addToOutput someEnumerator - -5. early termination: if you call 'finishWith': - - > a :: Snap () - > a = do - > modifyResponse $ setResponseStatus 500 "Internal Server Error" - > writeBS "500 error" - > r <- getResponse - > finishWith r - - then any subsequent processing will be skipped and supplied 'Response' value - will be returned from 'runSnap' as-is. - -6. access to the 'IO' monad through a 'MonadIO' instance: - - > a :: Snap () - > a = liftIO fireTheMissiles --} - - ------------------------------------------------------------------------------- -newtype Snap a = Snap { - unSnap :: StateT SnapState (Iteratee IO) (Maybe (Either Response a)) -} deriving Typeable - - ------------------------------------------------------------------------------- -data SnapState = SnapState - { _snapRequest :: Request - , _snapResponse :: Response - , _snapLogError :: ByteString -> IO () } - - ------------------------------------------------------------------------------- -instance Monad Snap where - (Snap m) >>= f = - Snap $ do - eth <- m - maybe (return Nothing) - (either (return . Just . Left) - (unSnap . f)) - eth - - return = Snap . return . Just . Right - fail = const $ Snap $ return Nothing - - ------------------------------------------------------------------------------- -instance MonadIO Snap where - liftIO m = Snap $ liftM (Just . Right) $ liftIO m - - ------------------------------------------------------------------------------- -instance MonadCatchIO Snap where - catch (Snap m) handler = Snap $ do - x <- try m - case x of - (Left e) -> let (Snap z) = handler e in z - (Right y) -> return y - - block (Snap m) = Snap $ block m - unblock (Snap m) = Snap $ unblock m - - ------------------------------------------------------------------------------- -instance MonadPlus Snap where - mzero = Snap $ return Nothing - - a `mplus` b = - Snap $ do - mb <- unSnap a - if isJust mb then return mb else unSnap b - - ------------------------------------------------------------------------------- -instance Functor Snap where - fmap = liftM - - ------------------------------------------------------------------------------- -instance Applicative Snap where - pure = return - (<*>) = ap - - ------------------------------------------------------------------------------- -instance Alternative Snap where - empty = mzero - (<|>) = mplus - - ------------------------------------------------------------------------------- -liftIter :: Iteratee IO a -> Snap a -liftIter i = Snap (lift i >>= return . Just . Right) - - ------------------------------------------------------------------------------- --- | Sends the request body through an iteratee (data consumer) and --- returns the result. -runRequestBody :: Iteratee IO a -> Snap a -runRequestBody iter = do - req <- getRequest - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - - -- make sure the iteratee consumes all of the output - let iter' = iter >>= (\a -> Iter.skipToEof >> return a) - - -- run the iteratee - result <- liftIter $ Iter.joinIM $ enum iter' - - -- stuff a new dummy enumerator into the request, so you can only try to - -- read the request body from the socket once - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . Iter.joinI . Iter.take 0 ) - - return result - - ------------------------------------------------------------------------------- --- | Returns the request body as a bytestring. -getRequestBody :: Snap L.ByteString -getRequestBody = liftM fromWrap $ runRequestBody stream2stream -{-# INLINE getRequestBody #-} - - ------------------------------------------------------------------------------- --- | Detaches the request body's 'Enumerator' from the 'Request' and --- returns it. You would want to use this if you needed to send the --- HTTP request body (transformed or otherwise) through to the output --- in O(1) space. (Examples: transcoding, \"echo\", etc) --- --- Normally Snap is careful to ensure that the request body is fully --- consumed after your web handler runs; this function is marked --- \"unsafe\" because it breaks this guarantee and leaves the --- responsibility up to you. If you don't fully consume the --- 'Enumerator' you get here, the next HTTP request in the pipeline --- (if any) will misparse. Be careful with exception handlers. -unsafeDetachRequestBody :: Snap (Enumerator a) -unsafeDetachRequestBody = do - req <- getRequest - let ioref = rqBody req - senum <- liftIO $ readIORef ioref - let (SomeEnumerator enum) = senum - liftIO $ writeIORef ioref - (SomeEnumerator $ return . Iter.joinI . Iter.take 0) - return enum - - ------------------------------------------------------------------------------- --- | Short-circuits a 'Snap' monad action early, storing the given --- 'Response' value in its state. -finishWith :: Response -> Snap () -finishWith = Snap . return . Just . Left -{-# INLINE finishWith #-} - - ------------------------------------------------------------------------------- --- | Fails out of a 'Snap' monad action. This is used to indicate --- that you choose not to handle the given request within the given --- handler. -pass :: Snap a -pass = empty - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only if the request's HTTP method matches --- the given method. -method :: Method -> Snap a -> Snap a -method m action = do - req <- getRequest - unless (rqMethod req == m) pass - action -{-# INLINE method #-} - - ------------------------------------------------------------------------------- --- Appends n bytes of the path info to the context path with a --- trailing slash. -updateContextPath :: Int -> Request -> Request -updateContextPath n req | n > 0 = req { rqContextPath = ctx - , rqPathInfo = pinfo } - | otherwise = req - where - ctx' = S.take n (rqPathInfo req) - ctx = S.concat [rqContextPath req, ctx', "/"] - pinfo = S.drop (n+1) (rqPathInfo req) - - ------------------------------------------------------------------------------- --- Runs a 'Snap' monad action only if the 'rqPathInfo' matches the given --- predicate. -pathWith :: (ByteString -> ByteString -> Bool) - -> ByteString - -> Snap a - -> Snap a -pathWith c p action = do - req <- getRequest - unless (c p (rqPathInfo req)) pass - localRequest (updateContextPath $ S.length p) action - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when the 'rqPathInfo' of the request --- starts with the given path. For example, --- --- > dir "foo" handler --- --- Will fail if 'rqPathInfo' is not \"@\/foo@\" or \"@\/foo\/...@\", and will --- add @\"foo\/\"@ to the handler's local 'rqContextPath'. -dir :: ByteString -- ^ path component to match - -> Snap a -- ^ handler to run - -> Snap a -dir = pathWith f - where - f dr pinfo = dr == x - where - (x,_) = S.break (=='/') pinfo -{-# INLINE dir #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is exactly --- equal to the given string. If the path matches, locally sets 'rqContextPath' --- to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\", and runs the given --- handler. -path :: ByteString -- ^ path to match against - -> Snap a -- ^ handler to run - -> Snap a -path = pathWith (==) -{-# INLINE path #-} - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action only when 'rqPathInfo' is empty. -ifTop :: Snap a -> Snap a -ifTop = path "" -{-# INLINE ifTop #-} - - ------------------------------------------------------------------------------- --- | Local Snap version of 'get'. -sget :: Snap SnapState -sget = Snap $ liftM (Just . Right) get -{-# INLINE sget #-} - - ------------------------------------------------------------------------------- --- | Local Snap monad version of 'modify'. -smodify :: (SnapState -> SnapState) -> Snap () -smodify f = Snap $ modify f >> return (Just $ Right ()) -{-# INLINE smodify #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Request' object out of the 'Snap' monad. -getRequest :: Snap Request -getRequest = liftM _snapRequest sget -{-# INLINE getRequest #-} - - ------------------------------------------------------------------------------- --- | Grabs the 'Response' object out of the 'Snap' monad. -getResponse :: Snap Response -getResponse = liftM _snapResponse sget -{-# INLINE getResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Response' object into the 'Snap' monad. -putResponse :: Response -> Snap () -putResponse r = smodify $ \ss -> ss { _snapResponse = r } -{-# INLINE putResponse #-} - - ------------------------------------------------------------------------------- --- | Puts a new 'Request' object into the 'Snap' monad. -putRequest :: Request -> Snap () -putRequest r = smodify $ \ss -> ss { _snapRequest = r } -{-# INLINE putRequest #-} - - ------------------------------------------------------------------------------- --- | Modifies the 'Request' object stored in a 'Snap' monad. -modifyRequest :: (Request -> Request) -> Snap () -modifyRequest f = smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss } -{-# INLINE modifyRequest #-} - - ------------------------------------------------------------------------------- --- | Modifes the 'Response' object stored in a 'Snap' monad. -modifyResponse :: (Response -> Response) -> Snap () -modifyResponse f = smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss } -{-# INLINE modifyResponse #-} - - ------------------------------------------------------------------------------- --- | Log an error message in the 'Snap' monad -logError :: ByteString -> Snap () -logError s = Snap $ gets _snapLogError >>= (\l -> liftIO $ l s) - >> return (Just $ Right ()) -{-# INLINE logError #-} - - ------------------------------------------------------------------------------- --- | Adds the output from the given enumerator to the 'Response' --- stored in the 'Snap' monad state. -addToOutput :: (forall a . Enumerator a) -- ^ output to add - -> Snap () -addToOutput enum = modifyResponse $ modifyResponseBody (>. enum) - - ------------------------------------------------------------------------------- --- | Adds the given strict 'ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeBS :: ByteString -> Snap () -writeBS s = addToOutput $ enumBS s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored in --- the 'Snap' monad state. -writeLBS :: L.ByteString -> Snap () -writeLBS s = addToOutput $ enumLBS s - - ------------------------------------------------------------------------------- --- | Adds the given strict 'T.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeText :: T.Text -> Snap () -writeText s = writeBS $ T.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Adds the given lazy 'LT.Text' to the body of the 'Response' stored in the --- 'Snap' monad state. -writeLazyText :: LT.Text -> Snap () -writeLazyText s = writeLBS $ LT.encodeUtf8 s - - ------------------------------------------------------------------------------- --- | Sets the output to be the contents of the specified file. --- --- Calling 'sendFile' will overwrite any output queued to be sent in the --- 'Response'. If the response body is not modified after the call to --- 'sendFile', Snap will use the efficient @sendfile()@ system call on --- platforms that support it. --- --- If the response body is modified (using 'modifyResponseBody'), the file will --- be read using @mmap()@. -sendFile :: FilePath -> Snap () -sendFile f = modifyResponse $ \r -> r { rspBody = SendFile f } - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' action with a locally-modified 'Request' state --- object. The 'Request' object in the Snap monad state after the call --- to localRequest will be unchanged. -localRequest :: (Request -> Request) -> Snap a -> Snap a -localRequest f m = do - req <- getRequest - - runAct req <|> (putRequest req >> pass) - - where - runAct req = do - modifyRequest f - result <- m - putRequest req - return result -{-# INLINE localRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Request' from state and hands it to the given action. -withRequest :: (Request -> Snap a) -> Snap a -withRequest = (getRequest >>=) -{-# INLINE withRequest #-} - - ------------------------------------------------------------------------------- --- | Fetches the 'Response' from state and hands it to the given action. -withResponse :: (Response -> Snap a) -> Snap a -withResponse = (getResponse >>=) -{-# INLINE withResponse #-} - - ------------------------------------------------------------------------------- --- | This exception is thrown if the handler you supply to 'runSnap' fails. -data NoHandlerException = NoHandlerException - deriving (Eq, Typeable) - - ------------------------------------------------------------------------------- -instance Show NoHandlerException where - show NoHandlerException = "No handler for request" - - ------------------------------------------------------------------------------- -instance Exception NoHandlerException - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' monad action in the 'Iteratee IO' monad. -runSnap :: Snap a - -> (ByteString -> IO ()) - -> Request - -> Iteratee IO (Request,Response) -runSnap (Snap m) logerr req = do - (r, ss') <- runStateT m ss - - e <- maybe (return $ Left fourohfour) - return - r - - -- is this a case of early termination? - let resp = case e of - Left x -> x - Right _ -> _snapResponse ss' - - return (_snapRequest ss', resp) - - where - fourohfour = setContentLength 3 $ - setResponseStatus 404 "Not Found" $ - modifyResponseBody (>. enumBS "404") $ - emptyResponse - - dresp = emptyResponse { rspHttpVersion = rqVersion req } - - ss = SnapState req dresp logerr -{-# INLINE runSnap #-} - - ------------------------------------------------------------------------------- -evalSnap :: Snap a - -> (ByteString -> IO ()) - -> Request - -> Iteratee IO a -evalSnap (Snap m) logerr req = do - (r, _) <- runStateT m ss - - e <- maybe (liftIO $ throwIO NoHandlerException) - return - r - - -- is this a case of early termination? - case e of - Left _ -> liftIO $ throwIO $ ErrorCall "no value" - Right x -> return x - where - dresp = emptyResponse { rspHttpVersion = rqVersion req } - ss = SnapState req dresp logerr -{-# INLINE evalSnap #-} - - - ------------------------------------------------------------------------------- --- | See 'rqParam'. Looks up a value for the given named parameter in the --- 'Request'. If more than one value was entered for the given parameter name, --- 'getParam' gloms the values together with: --- --- @ 'S.intercalate' \" \"@ --- -getParam :: ByteString -- ^ parameter name to look up - -> Snap (Maybe ByteString) -getParam k = do - rq <- getRequest - return $ liftM (S.intercalate " ") $ rqParam k rq - - -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Iteratee.html b/static/docs/0.2.4/snap-core/src/Snap-Iteratee.html deleted file mode 100644 index 68a3128..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Iteratee.html +++ /dev/null @@ -1,427 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} - --- | Snap Framework type aliases and utilities for iteratees. Note that as a --- convenience, this module also exports everything from @Data.Iteratee@ in the --- @iteratee@ library. --- --- /WARNING/: Note that all of these types are scheduled to change in the --- @darcs@ head version of the @iteratee@ library; John Lato et al. are working --- on a much improved iteratee formulation. - -module Snap.Iteratee - ( -- * Convenience aliases around types from @Data.Iteratee@ - Stream - , IterV - , Iteratee - , Enumerator - - -- * Re-export types and functions from @Data.Iteratee@ - , module Data.Iteratee - - -- * Helper functions - - -- ** Enumerators - , enumBS - , enumLBS - , enumFile - - -- ** Conversion to/from 'WrappedByteString' - , fromWrap - , toWrap - - -- ** Iteratee utilities - , takeExactly - , takeNoMoreThan - , countBytes - , bufferIteratee - , mkIterateeBuffer - , unsafeBufferIterateeWithBuffer - , unsafeBufferIteratee - ) where - ------------------------------------------------------------------------------- -import Control.Monad -import Control.Monad.CatchIO -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Unsafe as S -import qualified Data.ByteString.Lazy as L -import Data.IORef -import Data.Iteratee -#ifdef PORTABLE -import Data.Iteratee.IO (enumHandle) -#endif -import qualified Data.Iteratee.Base.StreamChunk as SC -import Data.Iteratee.WrappedByteString -import Data.Monoid (mappend) -import Foreign -import Foreign.C.Types -import GHC.ForeignPtr -import Prelude hiding (catch,drop) -import qualified Data.DList as D - -#ifdef PORTABLE -import Control.Monad.Trans (liftIO) -import System.IO -#else -import Control.Exception (SomeException) -import System.IO.Posix.MMap -#endif - ------------------------------------------------------------------------------- - -type Stream = StreamG WrappedByteString Word8 -type IterV m = IterGV WrappedByteString Word8 m -type Iteratee m = IterateeG WrappedByteString Word8 m -type Enumerator m a = Iteratee m a -> m (Iteratee m a) - - ------------------------------------------------------------------------------- -instance (Functor m, MonadCatchIO m) => - MonadCatchIO (IterateeG s el m) where - --catch :: Exception e => m a -> (e -> m a) -> m a - catch m handler = IterateeG $ \str -> do - ee <- try $ runIter m str - case ee of - (Left e) -> runIter (handler e) str - (Right v) -> return v - - --block :: m a -> m a - block m = IterateeG $ \str -> block $ runIter m str - unblock m = IterateeG $ \str -> unblock $ runIter m str - - ------------------------------------------------------------------------------- --- | Wraps an 'Iteratee', counting the number of bytes consumed by it. -countBytes :: (Monad m) => Iteratee m a -> Iteratee m (a, Int) -countBytes = go 0 - where - go !n iter = IterateeG $ f n iter - - f !n !iter ch@(Chunk ws) = do - iterv <- runIter iter ch - case iterv of - Done x rest -> let !n' = n + m - len rest - in return $! Done (x, n') rest - Cont i err -> return $ Cont ((go $! n + m) i) err - where - m = S.length $ unWrap ws - - len (EOF _) = 0 - len (Chunk s) = S.length $ unWrap s - - f !n !iter stream = do - iterv <- runIter iter stream - case iterv of - Done x rest -> return $ Done (x, n) rest - Cont i err -> return $ Cont (go n i) err - - ------------------------------------------------------------------------------- --- | Buffers an iteratee. --- --- Our enumerators produce a lot of little strings; rather than spending all --- our time doing kernel context switches for 4-byte write() calls, we buffer --- the iteratee to send 8KB at a time. -bufferIteratee :: (Monad m) => Enumerator m a -bufferIteratee = return . go (D.empty,0) - where - blocksize = 8192 - - --go :: (DList ByteString, Int) -> Iteratee m a -> Iteratee m a - go (!dl,!n) iter = IterateeG $! f (dl,n) iter - - --f :: (DList ByteString, Int) -> Iteratee m a -> Stream -> m (IterV m a) - f _ !iter ch@(EOF (Just _)) = runIter iter ch - f (!dl,_) !iter ch@(EOF Nothing) = do - iterv <- runIter iter $ Chunk big - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> runIter i ch - where - big = toWrap $ L.fromChunks [S.concat $ D.toList dl] - - f (!dl,!n) iter (Chunk ws) = - if n' > blocksize - then do - iterv <- runIter iter (Chunk big) - case iterv of - Done x rest -> return $ Done x rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> return $ Cont (go (D.empty,0) i) Nothing - else return $ Cont (go (dl',n') iter) Nothing - where - s = S.concat $ L.toChunks $ fromWrap ws - m = S.length s - n' = n+m - dl' = D.snoc dl s - big = toWrap $ L.fromChunks [S.concat $ D.toList dl'] - - -bUFSIZ :: Int -bUFSIZ = 8192 - - --- | Creates a buffer to be passed into 'unsafeBufferIterateeWithBuffer'. -mkIterateeBuffer :: IO (ForeignPtr CChar) -mkIterateeBuffer = mallocPlainForeignPtrBytes bUFSIZ - ------------------------------------------------------------------------------- --- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer which --- we'll re-use, meaning that if you hold on to any of the bytestring data --- passed into your iteratee (instead of, let's say, shoving it right out a --- socket) it'll get changed out from underneath you, breaking referential --- transparency. Use with caution! --- --- The IORef returned can be set to True to "cancel" buffering. We added this --- so that transfer-encoding: chunked (which needs its own buffer and therefore --- doesn't need /its/ output buffered) can switch the outer buffer off. --- -unsafeBufferIteratee :: Iteratee IO a -> IO (Iteratee IO a, IORef Bool) -unsafeBufferIteratee iter = do - buf <- mkIterateeBuffer - unsafeBufferIterateeWithBuffer buf iter - - ------------------------------------------------------------------------------- --- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer which --- we'll re-use, meaning that if you hold on to any of the bytestring data --- passed into your iteratee (instead of, let's say, shoving it right out a --- socket) it'll get changed out from underneath you, breaking referential --- transparency. Use with caution! --- --- This version accepts a buffer created by 'mkIterateeBuffer'. --- --- The IORef returned can be set to True to "cancel" buffering. We added this --- so that transfer-encoding: chunked (which needs its own buffer and therefore --- doesn't need /its/ output buffered) can switch the outer buffer off. --- -unsafeBufferIterateeWithBuffer :: ForeignPtr CChar - -> Iteratee IO a - -> IO (Iteratee IO a, IORef Bool) -unsafeBufferIterateeWithBuffer buf iteratee = do - esc <- newIORef False - return $! (start esc iteratee, esc) - - where - start esc iter = IterateeG $! checkRef esc iter - go bytesSoFar iter = - {-# SCC "unsafeBufferIteratee/go" #-} - IterateeG $! f bytesSoFar iter - - checkRef esc iter ch = do - quit <- readIORef esc - if quit - then runIter iter ch - else f 0 iter ch - - sendBuf n iter = - {-# SCC "unsafeBufferIteratee/sendBuf" #-} - withForeignPtr buf $ \ptr -> do - s <- S.unsafePackCStringLen (ptr, n) - runIter iter $ Chunk $ WrapBS s - - copy c@(EOF _) = c - copy (Chunk (WrapBS s)) = Chunk $ WrapBS $ S.copy s - - f _ iter ch@(EOF (Just _)) = runIter iter ch - - f !n iter ch@(EOF Nothing) = - if n == 0 - then runIter iter ch - else do - iterv <- sendBuf n iter - case iterv of - Done x rest -> return $ Done x $ copy rest - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> runIter i ch - - f !n iter (Chunk (WrapBS s)) = do - let m = S.length s - if m+n > bUFSIZ - then overflow n iter s m - else copyAndCont n iter s m - - copyAndCont n iter s m = - {-# SCC "unsafeBufferIteratee/copyAndCont" #-} do - S.unsafeUseAsCStringLen s $ \(p,sz) -> - withForeignPtr buf $ \bufp -> do - let b' = plusPtr bufp n - copyBytes b' p sz - - return $ Cont (go (n+m) iter) Nothing - - - overflow n iter s m = - {-# SCC "unsafeBufferIteratee/overflow" #-} do - let rest = bUFSIZ - n - let m2 = m - rest - let (s1,s2) = S.splitAt rest s - - S.unsafeUseAsCStringLen s1 $ \(p,_) -> - withForeignPtr buf $ \bufp -> do - let b' = plusPtr bufp n - copyBytes b' p rest - - iv <- sendBuf bUFSIZ iter - case iv of - Done x r -> return $ - Done x (copy r `mappend` (Chunk $ WrapBS s2)) - Cont i (Just e) -> return $ Cont i (Just e) - Cont i Nothing -> do - -- check the size of the remainder; if it's bigger than the - -- buffer size then just send it - if m2 >= bUFSIZ - then do - iv' <- runIter i (Chunk $ WrapBS s2) - case iv' of - Done x r -> return $ Done x (copy r) - Cont i' (Just e) -> return $ Cont i' (Just e) - Cont i' Nothing -> return $ Cont (go 0 i') Nothing - else copyAndCont 0 i s2 m2 - - ------------------------------------------------------------------------------- --- | Enumerates a strict bytestring. -enumBS :: (Monad m) => ByteString -> Enumerator m a -enumBS bs = enumPure1Chunk $ WrapBS bs -{-# INLINE enumBS #-} - - ------------------------------------------------------------------------------- --- | Enumerates a lazy bytestring. -enumLBS :: (Monad m) => L.ByteString -> Enumerator m a -enumLBS lbs = el chunks - where - el [] i = liftM liftI $ runIter i (EOF Nothing) - el (x:xs) i = do - i' <- liftM liftI $ runIter i (Chunk $ WrapBS x) - el xs i' - - chunks = L.toChunks lbs - - ------------------------------------------------------------------------------- --- | Converts a lazy bytestring to a wrapped bytestring. -toWrap :: L.ByteString -> WrappedByteString Word8 -toWrap = WrapBS . S.concat . L.toChunks -{-# INLINE toWrap #-} - - ------------------------------------------------------------------------------- --- | Converts a wrapped bytestring to a lazy bytestring. -fromWrap :: WrappedByteString Word8 -> L.ByteString -fromWrap = L.fromChunks . (:[]) . unWrap -{-# INLINE fromWrap #-} - - ------------------------------------------------------------------------------- --- | Reads n elements from a stream and applies the given iteratee to --- the stream of the read elements. Reads exactly n elements, and if --- the stream is short propagates an error. -takeExactly :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeExactly 0 iter = return iter -takeExactly n' iter = - if n' < 0 - then takeExactly 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeExactly n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - step n (Chunk str) = done (Chunk s1) (Chunk s2) - where (s1, s2) = SC.splitAt n str - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n (EOF Nothing) = return $ Cont undefined (Just (Err "short write")) - check n (Done x _) = drop n >> return (return x) - check n (Cont x Nothing) = takeExactly n x - check n (Cont _ (Just e)) = drop n >> throwErr e - done s1 s2 = liftM (flip Done s2) (runIter iter s1 >>= checkIfDone return) - - ------------------------------------------------------------------------------- --- | Reads up to n elements from a stream and applies the given iteratee to the --- stream of the read elements. If more than n elements are read, propagates an --- error. -takeNoMoreThan :: (SC.StreamChunk s el, Monad m) => - Int -> - EnumeratorN s el s el m a -takeNoMoreThan n' iter = - if n' < 0 - then takeNoMoreThan 0 iter - else IterateeG (step n') - where - step n chk@(Chunk str) - | SC.null str = return $ Cont (takeNoMoreThan n iter) Nothing - | SC.length str < n = liftM (flip Cont Nothing) inner - | otherwise = done (Chunk s1) (Chunk s2) - where inner = liftM (check (n - SC.length str)) (runIter iter chk) - (s1, s2) = SC.splitAt n str - - step _n (EOF (Just e)) = return $ Cont undefined (Just e) - step _n chk@(EOF Nothing) = do - v <- runIter iter chk - - case v of - (Done x s) -> return $ Done (return x) s - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont _ Nothing) -> return $ Cont (throwErr $ Err "premature EOF") Nothing - - check _ v@(Done _ _) = return $ liftI v - check n (Cont x Nothing) = takeNoMoreThan n x - check _ (Cont _ (Just e)) = throwErr e - - done _ (EOF _) = error "impossible" - done s1 s2@(Chunk s2') = do - v <- runIter iter s1 - case v of - (Done x s') -> return $ Done (return x) (s' `mappend` s2) - (Cont _ (Just e)) -> return $ Cont undefined (Just e) - (Cont i Nothing) -> - if SC.null s2' - then return $ Cont (takeNoMoreThan 0 i) Nothing - else return $ Cont undefined (Just $ Err "too many bytes") - - ------------------------------------------------------------------------------- -enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a) - -#ifdef PORTABLE - -enumFile fp iter = do - h <- liftIO $ openBinaryFile fp ReadMode - i' <- enumHandle h iter - return $ do - x <- i' - liftIO (hClose h) - return x - -#else - -enumFile fp iter = do - es <- (try $ - liftM WrapBS $ - unsafeMMapFile fp) :: IO (Either SomeException (WrappedByteString Word8)) - - case es of - (Left e) -> return $ throwErr $ Err $ "IO error" ++ show e - (Right s) -> liftM liftI $ runIter iter $ Chunk s - -#endif -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Types.html b/static/docs/0.2.4/snap-core/src/Snap-Types.html deleted file mode 100644 index ecbe5f0..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Types.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - -
{-| - -This module contains the core type definitions, class instances, and functions -for HTTP as well as the 'Snap' monad, which is used for web handlers. - --} -module Snap.Types - ( - -- * The Snap Monad - Snap - , runSnap - , NoHandlerException(..) - - -- ** Functions for control flow and early termination - , finishWith - , pass - - -- ** Routing - , method - , path - , dir - , ifTop - , route - , routeLocal - - -- ** Access to state - , getRequest - , getResponse - , putRequest - , putResponse - , modifyRequest - , modifyResponse - , localRequest - , withRequest - , withResponse - - -- ** Logging - , logError - - -- ** Grabbing request bodies - , runRequestBody - , getRequestBody - , unsafeDetachRequestBody - -- * HTTP Datatypes and Functions - -- $httpDoc - -- - , Request - , Response - , Headers - , HasHeaders(..) - , Params - , Method(..) - , Cookie(..) - , HttpVersion - - -- ** Headers - , addHeader - , setHeader - , getHeader - - -- ** Requests - , rqServerName - , rqServerPort - , rqRemoteAddr - , rqRemotePort - , rqLocalAddr - , rqLocalHostname - , rqIsSecure - , rqContentLength - , rqMethod - , rqVersion - , rqCookies - , rqPathInfo - , rqContextPath - , rqURI - , rqQueryString - , rqParams - , rqParam - , getParam - , rqModifyParams - , rqSetParam - - -- ** Responses - , emptyResponse - , setResponseStatus - , rspStatus - , rspStatusReason - , setContentType - , addCookie - , setContentLength - , clearContentLength - - -- *** Response I/O - , setResponseBody - , modifyResponseBody - , addToOutput - , writeBS - , writeLazyText - , writeText - , writeLBS - , sendFile - - -- * Iteratee - , Enumerator - - -- * HTTP utilities - , formatHttpTime - , parseHttpTime - , urlEncode - , urlDecode - ) where - ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types -import Snap.Internal.Routing -import Snap.Internal.Types ------------------------------------------------------------------------------- - --- $httpDoc --- HTTP-related datatypes: 'Request', 'Response', 'Cookie', etc. -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Util-FileServe.html b/static/docs/0.2.4/snap-core/src/Snap-Util-FileServe.html deleted file mode 100644 index cd3b3bc..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Util-FileServe.html +++ /dev/null @@ -1,274 +0,0 @@ - - - - -
{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | Contains web handlers to serve files from a directory. -module Snap.Util.FileServe -( - getSafePath -, fileServe -, fileServe' -, fileServeSingle -, fileServeSingle' -, defaultMimeTypes -, MimeMap -) where - ------------------------------------------------------------------------------- -import Control.Monad -import Control.Monad.Trans -import qualified Data.ByteString.Char8 as S -import Data.ByteString.Char8 (ByteString) -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import System.Directory -import System.FilePath -import System.PosixCompat.Files - ------------------------------------------------------------------------------- -import Snap.Types - - ------------------------------------------------------------------------------- --- | A type alias for MIME type -type MimeMap = Map FilePath ByteString - - ------------------------------------------------------------------------------- --- | The default set of mime type mappings we use when serving files. Its --- value: --- --- > Map.fromList [ --- > ( ".asc" , "text/plain" ), --- > ( ".asf" , "video/x-ms-asf" ), --- > ( ".asx" , "video/x-ms-asf" ), --- > ( ".avi" , "video/x-msvideo" ), --- > ( ".bz2" , "application/x-bzip" ), --- > ( ".c" , "text/plain" ), --- > ( ".class" , "application/octet-stream" ), --- > ( ".conf" , "text/plain" ), --- > ( ".cpp" , "text/plain" ), --- > ( ".css" , "text/css" ), --- > ( ".cxx" , "text/plain" ), --- > ( ".dtd" , "text/xml" ), --- > ( ".dvi" , "application/x-dvi" ), --- > ( ".gif" , "image/gif" ), --- > ( ".gz" , "application/x-gzip" ), --- > ( ".hs" , "text/plain" ), --- > ( ".htm" , "text/html" ), --- > ( ".html" , "text/html" ), --- > ( ".jar" , "application/x-java-archive" ), --- > ( ".jpeg" , "image/jpeg" ), --- > ( ".jpg" , "image/jpeg" ), --- > ( ".js" , "text/javascript" ), --- > ( ".log" , "text/plain" ), --- > ( ".m3u" , "audio/x-mpegurl" ), --- > ( ".mov" , "video/quicktime" ), --- > ( ".mp3" , "audio/mpeg" ), --- > ( ".mpeg" , "video/mpeg" ), --- > ( ".mpg" , "video/mpeg" ), --- > ( ".ogg" , "application/ogg" ), --- > ( ".pac" , "application/x-ns-proxy-autoconfig" ), --- > ( ".pdf" , "application/pdf" ), --- > ( ".png" , "image/png" ), --- > ( ".ps" , "application/postscript" ), --- > ( ".qt" , "video/quicktime" ), --- > ( ".sig" , "application/pgp-signature" ), --- > ( ".spl" , "application/futuresplash" ), --- > ( ".swf" , "application/x-shockwave-flash" ), --- > ( ".tar" , "application/x-tar" ), --- > ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), --- > ( ".tar.gz" , "application/x-tgz" ), --- > ( ".tbz" , "application/x-bzip-compressed-tar" ), --- > ( ".text" , "text/plain" ), --- > ( ".tgz" , "application/x-tgz" ), --- > ( ".torrent" , "application/x-bittorrent" ), --- > ( ".txt" , "text/plain" ), --- > ( ".wav" , "audio/x-wav" ), --- > ( ".wax" , "audio/x-ms-wax" ), --- > ( ".wma" , "audio/x-ms-wma" ), --- > ( ".wmv" , "video/x-ms-wmv" ), --- > ( ".xbm" , "image/x-xbitmap" ), --- > ( ".xml" , "text/xml" ), --- > ( ".xpm" , "image/x-xpixmap" ), --- > ( ".xwd" , "image/x-xwindowdump" ), --- > ( ".zip" , "application/zip" ) ] --- -defaultMimeTypes :: MimeMap -defaultMimeTypes = Map.fromList [ - ( ".asc" , "text/plain" ), - ( ".asf" , "video/x-ms-asf" ), - ( ".asx" , "video/x-ms-asf" ), - ( ".avi" , "video/x-msvideo" ), - ( ".bz2" , "application/x-bzip" ), - ( ".c" , "text/plain" ), - ( ".class" , "application/octet-stream" ), - ( ".conf" , "text/plain" ), - ( ".cpp" , "text/plain" ), - ( ".css" , "text/css" ), - ( ".cxx" , "text/plain" ), - ( ".dtd" , "text/xml" ), - ( ".dvi" , "application/x-dvi" ), - ( ".gif" , "image/gif" ), - ( ".gz" , "application/x-gzip" ), - ( ".hs" , "text/plain" ), - ( ".htm" , "text/html" ), - ( ".html" , "text/html" ), - ( ".jar" , "application/x-java-archive" ), - ( ".jpeg" , "image/jpeg" ), - ( ".jpg" , "image/jpeg" ), - ( ".js" , "text/javascript" ), - ( ".log" , "text/plain" ), - ( ".m3u" , "audio/x-mpegurl" ), - ( ".mov" , "video/quicktime" ), - ( ".mp3" , "audio/mpeg" ), - ( ".mpeg" , "video/mpeg" ), - ( ".mpg" , "video/mpeg" ), - ( ".ogg" , "application/ogg" ), - ( ".pac" , "application/x-ns-proxy-autoconfig" ), - ( ".pdf" , "application/pdf" ), - ( ".png" , "image/png" ), - ( ".ps" , "application/postscript" ), - ( ".qt" , "video/quicktime" ), - ( ".sig" , "application/pgp-signature" ), - ( ".spl" , "application/futuresplash" ), - ( ".swf" , "application/x-shockwave-flash" ), - ( ".tar" , "application/x-tar" ), - ( ".tar.bz2" , "application/x-bzip-compressed-tar" ), - ( ".tar.gz" , "application/x-tgz" ), - ( ".tbz" , "application/x-bzip-compressed-tar" ), - ( ".text" , "text/plain" ), - ( ".tgz" , "application/x-tgz" ), - ( ".torrent" , "application/x-bittorrent" ), - ( ".ttf" , "application/x-font-truetype" ), - ( ".txt" , "text/plain" ), - ( ".wav" , "audio/x-wav" ), - ( ".wax" , "audio/x-ms-wax" ), - ( ".wma" , "audio/x-ms-wma" ), - ( ".wmv" , "video/x-ms-wmv" ), - ( ".xbm" , "image/x-xbitmap" ), - ( ".xml" , "text/xml" ), - ( ".xpm" , "image/x-xpixmap" ), - ( ".xwd" , "image/x-xwindowdump" ), - ( ".zip" , "application/zip" ) ] - ------------------------------------------------------------------------------- --- | Gets a path from the 'Request' using 'rqPathInfo' and makes sure it is --- safe to use for opening files. A path is safe if it is a relative path --- and has no ".." elements to escape the intended directory structure. -getSafePath :: Snap FilePath -getSafePath = do - req <- getRequest - let p = S.unpack $ rqPathInfo req - - -- check that we don't have any sneaky .. paths - let dirs = splitDirectories p - when (elem ".." dirs) pass - return p - - ------------------------------------------------------------------------------- --- | Serves files out of the given directory. The relative path given in --- 'rqPathInfo' is searched for the given file, and the file is served with the --- appropriate mime type if it is found. Absolute paths and \"@..@\" are prohibited --- to prevent files from being served from outside the sandbox. --- --- Uses 'defaultMimeTypes' to determine the @Content-Type@ based on the file's --- extension. -fileServe :: FilePath -- ^ root directory - -> Snap () -fileServe = fileServe' defaultMimeTypes -{-# INLINE fileServe #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServe', with control over the MIME mapping used. -fileServe' :: MimeMap -- ^ MIME type mapping - -> FilePath -- ^ root directory - -> Snap () -fileServe' mm root = do - sp <- getSafePath - let fp = root </> sp - - -- check that the file exists - liftIO (doesFileExist fp) >>= flip unless pass - - let fn = takeFileName fp - let mime = fileType mm fn - fileServeSingle' mime fp -{-# INLINE fileServe' #-} - - ------------------------------------------------------------------------------- --- | Serves a single file specified by a full or relative path. The --- path restrictions on fileServe don't apply to this function since --- the path is not being supplied by the user. -fileServeSingle :: FilePath -- ^ path to file - -> Snap () -fileServeSingle fp = - fileServeSingle' (fileType defaultMimeTypes (takeFileName fp)) fp -{-# INLINE fileServeSingle #-} - - ------------------------------------------------------------------------------- --- | Same as 'fileServeSingle', with control over the MIME mapping used. -fileServeSingle' :: ByteString -- ^ MIME type mapping - -> FilePath -- ^ path to file - -> Snap () -fileServeSingle' mime fp = do - req <- getRequest - - let mbH = getHeader "if-modified-since" req - mbIfModified <- liftIO $ case mbH of - Nothing -> return Nothing - (Just s) -> liftM Just $ parseHttpTime s - - -- check modification time and bug out early if the file is not modified. - filestat <- liftIO $ getFileStatus fp - let mt = modificationTime filestat - maybe (return ()) (chkModificationTime mt) mbIfModified - - let sz = fromEnum $ fileSize filestat - lm <- liftIO $ formatHttpTime mt - - modifyResponse $ setHeader "Last-Modified" lm - . setContentType mime - . setContentLength sz - sendFile fp - - where - -------------------------------------------------------------------------- - chkModificationTime mt lt = when (mt <= lt) notModified - - -------------------------------------------------------------------------- - notModified = finishWith $ - setResponseStatus 304 "Not Modified" emptyResponse - - ------------------------------------------------------------------------------- -fileType :: MimeMap -> FilePath -> ByteString -fileType mm f = - if null ext - then defaultMimeType - else fromMaybe (fileType mm (drop 1 ext)) - mbe - - where - ext = takeExtensions f - mbe = Map.lookup ext mm - - ------------------------------------------------------------------------------- -defaultMimeType :: ByteString -defaultMimeType = "application/octet-stream" -- diff --git a/static/docs/0.2.4/snap-core/src/Snap-Util-GZip.html b/static/docs/0.2.4/snap-core/src/Snap-Util-GZip.html deleted file mode 100644 index 83ec5df..0000000 --- a/static/docs/0.2.4/snap-core/src/Snap-Util-GZip.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - -
{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Util.GZip -( withCompression -, withCompression' ) where - -import qualified Codec.Compression.GZip as GZip -import qualified Codec.Compression.Zlib as Zlib -import Control.Concurrent -import Control.Applicative hiding (many) -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.Attoparsec.Char8 hiding (Done) -import qualified Data.Attoparsec.Char8 as Atto -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Char8 (ByteString) -import Data.Iteratee.WrappedByteString -import Data.Maybe -import qualified Data.Set as Set -import Data.Set (Set) -import Data.Typeable -import Prelude hiding (catch, takeWhile) - ------------------------------------------------------------------------------- -import Snap.Internal.Debug -import Snap.Iteratee hiding (Enumerator) -import Snap.Types - - ------------------------------------------------------------------------------- --- | Runs a 'Snap' web handler with compression if available. --- --- If the client has indicated support for @gzip@ or @compress@ in its --- @Accept-Encoding@ header, and the @Content-Type@ in the response is one of --- the following types: --- --- * @application/x-javascript@ --- --- * @text/css@ --- --- * @text/html@ --- --- * @text/javascript@ --- --- * @text/plain@ --- --- * @text/xml@ --- --- * @application/x-font-truetype@ --- --- Then the given handler's output stream will be compressed, --- @Content-Encoding@ will be set in the output headers, and the --- @Content-Length@ will be cleared if it was set. (We can't process the stream --- in O(1) space if the length is known beforehand.) --- --- The wrapped handler will be run to completion, and then the 'Response' --- that's contained within the 'Snap' monad state will be passed to --- 'finishWith' to prevent further processing. --- -withCompression :: Snap a -- ^ the web handler to run - -> Snap () -withCompression = withCompression' compressibleMimeTypes - - ------------------------------------------------------------------------------- --- | The same as 'withCompression', with control over which MIME types to --- compress. -withCompression' :: Set ByteString - -- ^ set of compressible MIME types - -> Snap a - -- ^ the web handler to run - -> Snap () -withCompression' mimeTable action = do - _ <- action - resp <- getResponse - - -- If a content-encoding is already set, do nothing. This prevents - -- "withCompression $ withCompression m" from ruining your day. - if isJust $ getHeader "Content-Encoding" resp - then return () - else do - let mbCt = getHeader "Content-Type" resp - - debug $ "withCompression', content-type is " ++ show mbCt - - case mbCt of - (Just ct) -> if Set.member ct mimeTable - then chkAcceptEncoding - else return () - _ -> return () - - - getResponse >>= finishWith - - where - chkAcceptEncoding :: Snap () - chkAcceptEncoding = do - req <- getRequest - debug $ "checking accept-encoding" - let mbAcc = getHeader "Accept-Encoding" req - debug $ "accept-encoding is " ++ show mbAcc - let s = fromMaybe "" mbAcc - - types <- liftIO $ parseAcceptEncoding s - - chooseType types - - - chooseType [] = return () - chooseType ("gzip":_) = gzipCompression - chooseType ("compress":_) = compressCompression - chooseType ("x-gzip":_) = gzipCompression - chooseType ("x-compress":_) = compressCompression - chooseType (_:xs) = chooseType xs - - ------------------------------------------------------------------------------- --- private following ------------------------------------------------------------------------------- - - ------------------------------------------------------------------------------- -compressibleMimeTypes :: Set ByteString -compressibleMimeTypes = Set.fromList [ "application/x-font-truetype" - , "application/x-javascript" - , "text/css" - , "text/html" - , "text/javascript" - , "text/plain" - , "text/xml" ] - - - - ------------------------------------------------------------------------------- -gzipCompression :: Snap () -gzipCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "gzip" . - clearContentLength . - modifyResponseBody gcompress - - ------------------------------------------------------------------------------- -compressCompression :: Snap () -compressCompression = modifyResponse f - where - f = setHeader "Content-Encoding" "compress" . - clearContentLength . - modifyResponseBody ccompress - - ------------------------------------------------------------------------------- -gcompress :: forall a . Enumerator a -> Enumerator a -gcompress = compressEnumerator GZip.compress - - ------------------------------------------------------------------------------- -ccompress :: forall a . Enumerator a -> Enumerator a -ccompress = compressEnumerator Zlib.compress - - ------------------------------------------------------------------------------- -compressEnumerator :: forall a . - (L.ByteString -> L.ByteString) - -> Enumerator a - -> Enumerator a -compressEnumerator compFunc enum iteratee = do - writeEnd <- newChan - readEnd <- newChan - tid <- forkIO $ threadProc readEnd writeEnd - - enum (IterateeG $ f readEnd writeEnd tid iteratee) - - where - -------------------------------------------------------------------------- - streamFinished :: Stream -> Bool - streamFinished (EOF _) = True - streamFinished (Chunk _) = False - - - -------------------------------------------------------------------------- - consumeSomeOutput :: Chan Stream - -> Iteratee IO a - -> IO (Iteratee IO a) - consumeSomeOutput writeEnd iter = do - e <- isEmptyChan writeEnd - if e - then return iter - else do - ch <- readChan writeEnd - - iter' <- liftM liftI $ runIter iter ch - if (streamFinished ch) - then return iter' - else consumeSomeOutput writeEnd iter' - - - -------------------------------------------------------------------------- - consumeRest :: Chan Stream - -> Iteratee IO a - -> IO (IterV IO a) - consumeRest writeEnd iter = do - ch <- readChan writeEnd - - iv <- runIter iter ch - if (streamFinished ch) - then return iv - else consumeRest writeEnd $ liftI iv - - - -------------------------------------------------------------------------- - f readEnd writeEnd tid i (EOF Nothing) = do - writeChan readEnd Nothing - x <- consumeRest writeEnd i - killThread tid - return x - - f _ _ tid i ch@(EOF (Just _)) = do - x <- runIter i ch - killThread tid - return x - - f readEnd writeEnd tid i (Chunk s') = do - let s = unWrap s' - writeChan readEnd $ Just s - i' <- consumeSomeOutput writeEnd i - return $ Cont (IterateeG $ f readEnd writeEnd tid i') Nothing - - - -------------------------------------------------------------------------- - threadProc :: Chan (Maybe ByteString) - -> Chan Stream - -> IO () - threadProc readEnd writeEnd = do - stream <- getChanContents readEnd - let bs = L.fromChunks $ streamToChunks stream - - let output = L.toChunks $ compFunc bs - let runIt = do - mapM_ (writeChan writeEnd . toChunk) output - writeChan writeEnd $ EOF Nothing - - runIt `catch` \(e::SomeException) -> - writeChan writeEnd $ EOF (Just $ Err $ show e) - - - -------------------------------------------------------------------------- - streamToChunks [] = [] - streamToChunks (Nothing:_) = [] - streamToChunks ((Just x):xs) = x:(streamToChunks xs) - - - -------------------------------------------------------------------------- - toChunk = Chunk . WrapBS - - ------------------------------------------------------------------------------- -fullyParse :: ByteString -> Parser a -> Either String a -fullyParse s p = - case r' of - (Fail _ _ e) -> Left e - (Partial _) -> Left "parse failed" - (Atto.Done _ x) -> Right x - where - r = parse p s - r' = feed r "" - - ------------------------------------------------------------------------------- --- We're not gonna bother with quality values; we'll do gzip or compress in --- that order. -acceptParser :: Parser [ByteString] -acceptParser = do - xs <- option [] $ (:[]) <$> encoding - ys <- many (char ',' *> encoding) - endOfInput - return $ xs ++ ys - where - encoding = skipSpace *> c <* skipSpace - - c = do - x <- coding - option () qvalue - return x - - qvalue = do - skipSpace - char ';' - skipSpace - char 'q' - skipSpace - char '=' - float - return () - - coding = string "*" <|> takeWhile isCodingChar - - isCodingChar ch = isDigit ch || isAlpha_ascii ch || ch == '-' || ch == '_' - - float = takeWhile isDigit >> - option () (char '.' >> takeWhile isDigit >> pure ()) - - ------------------------------------------------------------------------------- -data BadAcceptEncodingException = BadAcceptEncodingException - deriving (Typeable) - - ------------------------------------------------------------------------------- -instance Show BadAcceptEncodingException where - show BadAcceptEncodingException = "bad 'accept-encoding' header" - - ------------------------------------------------------------------------------- -instance Exception BadAcceptEncodingException - - ------------------------------------------------------------------------------- -parseAcceptEncoding :: ByteString -> IO [ByteString] -parseAcceptEncoding s = - case r of - Left _ -> throwIO BadAcceptEncodingException - Right x -> return x - where - r = fullyParse s acceptParser - -- diff --git a/static/docs/0.2.4/snap-core/src/hscolour.css b/static/docs/0.2.4/snap-core/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.2.4/snap-core/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/0.2.4/snap-server/Snap-Http-Server-Config.html b/static/docs/0.2.4/snap-server/Snap-Http-Server-Config.html deleted file mode 100644 index d60f3d6..0000000 --- a/static/docs/0.2.4/snap-server/Snap-Http-Server-Config.html +++ /dev/null @@ -1,287 +0,0 @@ - - -
| ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||
| ||||||||||||||||
Description | ||||||||||||||||
The Snap HTTP server is a high performance, epoll-enabled, iteratee-based - web server library written in Haskell. Together with the snap-core library - upon which it depends, it provides a clean and efficient Haskell programming - interface to the HTTP protocol. - | ||||||||||||||||
Synopsis | ||||||||||||||||
| ||||||||||||||||
Documentation | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||
| ||||||||||||||||||
Synopsis | ||||||||||||||||||
| ||||||||||||||||||
Documentation | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Prepares a log message with the time prepended. - | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
Creates a new logger, logging to the given file. If the file argument is - "-", then log to stdout; if it's "stderr" then we log to stderr, - otherwise we log to a regular file in append mode. The file is closed and - re-opened every 15 minutes to facilitate external log rotation. - | ||||||||||||||||||
| ||||||||||||||||||
Sends out a log message verbatim with a newline appended. Note: - if you want a fancy log message you'll have to format it yourself - (or use combinedLogEntry). - | ||||||||||||||||||
| ||||||||||||||||||
Kills a logger thread, causing any unwritten contents to be - flushed out to disk - | ||||||||||||||||||
Produced by Haddock version 2.6.1 |
| ||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||
snap-server-0.2.4: A fast, iteratee-based, epoll-enabled web server for the Snap Framework | ||||||||||||||||||||||||||||||
This is the first developer prerelease of the Snap framework. Snap is a -simple and fast web development framework and server written in Haskell. For -more information or to download the latest version, you can visit the Snap -project website at http://snapframework.com/. - The Snap HTTP server is a high performance, epoll-enabled, iteratee-based web -server library written in Haskell. Together with the snap-core library upon -which it depends, it provides a clean and efficient Haskell programming -interface to the HTTP protocol. - Higher-level facilities for building web applications (like user/session -management, component interfaces, data modeling, etc.) are planned but not -yet implemented, so this release will mostly be of interest for those who: -
| ||||||||||||||||||||||||||||||
Modules | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
Produced by Haddock version 2.6.1 |
module Paths_snap_server ( - version, - getBinDir, getLibDir, getDataDir, getLibexecDir, - getDataFileName - ) where - -import Data.Version (Version(..)) -import System.Environment (getEnv) - -version :: Version -version = Version {versionBranch = [0,2,4], versionTags = []} - -bindir, libdir, datadir, libexecdir :: FilePath - -bindir = "/Users/greg/.cabal/bin" -libdir = "/Users/greg/.cabal/lib/snap-server-0.2.4/ghc-6.12.2" -datadir = "/Users/greg/.cabal/share/snap-server-0.2.4" -libexecdir = "/Users/greg/.cabal/libexec" - -getBinDir, getLibDir, getDataDir, getLibexecDir :: IO FilePath -getBinDir = catch (getEnv "snap_server_bindir") (\_ -> return bindir) -getLibDir = catch (getEnv "snap_server_libdir") (\_ -> return libdir) -getDataDir = catch (getEnv "snap_server_datadir") (\_ -> return datadir) -getLibexecDir = catch (getEnv "snap_server_libexecdir") (\_ -> return libexecdir) - -getDataFileName :: FilePath -> IO FilePath -getDataFileName name = do - dir <- getDataDir - return (dir ++ "/" ++ name) -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Http-Server-Config.html b/static/docs/0.2.4/snap-server/src/Snap-Http-Server-Config.html deleted file mode 100644 index 5c11ecf..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Http-Server-Config.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Snap.Http.Server.Config - ( Config(..) - , readConfigFromCmdLineArgs - ) where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as B -import Data.ByteString.Internal (c2w) -import Data.ByteString.Char8 () -import Data.Maybe -import Data.Monoid -import System.Console.GetOpt -import System.Environment -import System.Exit -import System.IO - -data Config = Config - { localHostname :: !ByteString - , bindAddress :: !ByteString - , listenPort :: !Int - , accessLog :: !(Maybe FilePath) - , errorLog :: !(Maybe FilePath) - } deriving (Show) - - -data Flag = Flag - { flagLocalHost :: Maybe String - , flagBindAddress :: Maybe String - , flagPort :: Maybe Int - , flagAccessLog :: Maybe String - , flagErrorLog :: Maybe String - , flagUsage :: Bool - } - -instance Monoid Flag where - mempty = Flag Nothing Nothing Nothing Nothing Nothing False - - (Flag a1 b1 c1 d1 e1 f1) `mappend` (Flag a2 b2 c2 d2 e2 f2) = - Flag (getLast $ Last a1 `mappend` Last a2) - (getLast $ Last b1 `mappend` Last b2) - (getLast $ Last c1 `mappend` Last c2) - (getLast $ Last d1 `mappend` Last d2) - (getLast $ Last e1 `mappend` Last e2) - (f1 || f2) - -flagLH :: String -> Flag -flagLH s = mempty { flagLocalHost = Just s } - -flagBA :: String -> Flag -flagBA s = mempty { flagBindAddress = Just s } - -flagPt :: String -> Flag -flagPt p = mempty { flagPort = Just (read p) } - -flagAL :: String -> Flag -flagAL s = mempty { flagAccessLog = Just s } - -flagEL :: String -> Flag -flagEL s = mempty { flagErrorLog = Just s } - -flagHelp :: Flag -flagHelp = mempty { flagUsage = True } - -fromStr :: String -> ByteString -fromStr = B.pack . map c2w - -flags2config :: Flag -> Config -flags2config (Flag a b c d e _) = - Config (maybe "localhost" fromStr a) - (maybe "*" fromStr b) - (fromMaybe 8888 c) - d - e - - -options :: [OptDescr Flag] -options = - [ Option "l" ["localHostname"] - (ReqArg flagLH "STR") - "local hostname, default 'localhost'" - , Option "p" ["listenPort"] - (ReqArg flagPt "NUM") - "port to listen on, default 8888" - , Option "b" ["bindAddress"] - (ReqArg flagBA "STR") - "address to bind to, default '*'" - , Option "a" ["accessLog"] - (ReqArg flagAL "STR") - "access log in the 'combined' format, optional" - , Option "e" ["errorLog"] - (ReqArg flagEL "STR") - "error log, optional" - , Option "h" ["help"] - (NoArg flagHelp) - "display this usage statement" ] - - -readConfigFromCmdLineArgs :: String -- ^ application description, e.g. - -- \"Foo applet v0.2\" - -> IO Config -readConfigFromCmdLineArgs appName = do - argv <- getArgs - progName <- getProgName - - case getOpt Permute options argv of - (f,_,[] ) -> withFlags progName f - (_,_,errs) -> bombout progName errs - where - bombout progName errs = do - let hdr = appName ++ "\n\nUsage: " ++ progName ++ " [OPTIONS]" - let msg = concat errs ++ usageInfo hdr options - hPutStrLn stderr msg - exitFailure - - withFlags progName fs = do - let f = mconcat fs - if flagUsage f - then bombout progName [] - else return $ flags2config f -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Http-Server.html b/static/docs/0.2.4/snap-server/src/Snap-Http-Server.html deleted file mode 100644 index 3c0f8bd..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Http-Server.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - -
-- | The Snap HTTP server is a high performance, epoll-enabled, iteratee-based --- web server library written in Haskell. Together with the @snap-core@ library --- upon which it depends, it provides a clean and efficient Haskell programming --- interface to the HTTP protocol. -module Snap.Http.Server -( - httpServe -, snapServerVersion -) where - -import Data.ByteString (ByteString) -import Snap.Types -import qualified Snap.Internal.Http.Server as Int - - ------------------------------------------------------------------------------- -snapServerVersion :: ByteString -snapServerVersion = Int.snapServerVersion - - ------------------------------------------------------------------------------- --- | Starts serving HTTP requests on the given port using the given handler. --- This function never returns; to shut down the HTTP server, kill the --- controlling thread. -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the (optional) access log - -> Maybe FilePath -- ^ path to the (optional) error log - -> Snap () -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alog elog handler = - Int.httpServe bindAddress bindPort localHostname alog elog handler' - where - handler' = runSnap handler -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Parser.html b/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Parser.html deleted file mode 100644 index 70e287f..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Parser.html +++ /dev/null @@ -1,438 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} - -module Snap.Internal.Http.Parser - ( IRequest(..) - , parseRequest - , readChunkedTransferEncoding - , parserToIteratee - , parseCookie - , parseUrlEncoded - , writeChunkedTransferEncoding - , strictize - ) where - - ------------------------------------------------------------------------------- -import Control.Applicative -import Control.Arrow (second) -import Control.Monad (liftM) -import Control.Monad.Trans -import Data.Attoparsec hiding (many, Result(..)) -import Data.Attoparsec.Iteratee -import Data.Bits -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Lazy as L -import qualified Data.ByteString.Nums.Careless.Hex as Cvt -import Data.Char -import Data.List (foldl') -import Data.Int -import Data.Iteratee.WrappedByteString -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (catMaybes) -import qualified Data.Vector.Unboxed as Vec -import Data.Vector.Unboxed (Vector) -import Data.Word (Word8, Word64) -import Foreign.C.Types -import Foreign.ForeignPtr -import Prelude hiding (take, takeWhile) ------------------------------------------------------------------------------- -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Iteratee hiding (take, foldl', filter) - - - ------------------------------------------------------------------------------- --- | an internal version of the headers part of an HTTP request -data IRequest = IRequest - { iMethod :: Method - , iRequestUri :: ByteString - , iHttpVersion :: (Int,Int) - , iRequestHeaders :: [(ByteString, ByteString)] - } - -instance Show IRequest where - show (IRequest m u v r) = - concat [ show m - , " " - , show u - , " " - , show v - , " " - , show r ] - ------------------------------------------------------------------------------- -parseRequest :: (Monad m) => Iteratee m (Maybe IRequest) -parseRequest = parserToIteratee pRequest - - -readChunkedTransferEncoding :: (Monad m) => Enumerator m a -readChunkedTransferEncoding iter = do - i <- chunkParserToEnumerator (parserToIteratee pGetTransferChunk) - iter - - return i - - -toHex :: Int64 -> ByteString -toHex !i' = S.reverse s - where - !i = abs i' - (!s,_) = S.unfoldrN 16 f (fromIntegral i) - - f :: Word64 -> Maybe (Word8, Word64) - f d = if d == 0 - then Nothing - else Just (ch, theRest) - - where - low4 = fromIntegral $ d .&. 0xf - ch = if low4 >= 10 - then c2w 'a' + low4 - 10 - else c2w '0' + low4 - theRest = (d .&. (complement 0xf)) `shiftR` 4 - - --- | Given an iteratee, produces a new one that wraps chunks sent to it with a --- chunked transfer-encoding. Example usage: --- --- FIXME: sample output no longer looks like this, we buffer now --- --- > > (writeChunkedTransferEncoding --- > (enumLBS (L.fromChunks ["foo","bar","quux"])) --- > stream2stream) >>= --- > run >>= --- > return . fromWrap --- > --- > Chunk "3\r\nfoo\r\n3\r\nbar\r\n4\r\nquux\r\n0\r\n\r\n" Empty --- -writeChunkedTransferEncoding :: ForeignPtr CChar - -> Enumerator IO a - -> Enumerator IO a -writeChunkedTransferEncoding buf enum it = do - i' <- wrap it - (i,_) <- unsafeBufferIterateeWithBuffer buf i' - enum i - - where - wrap iter = return $ IterateeG $ \s -> - case s of - (EOF Nothing) -> do - v <- runIter iter (Chunk $ toWrap "0\r\n\r\n") - i <- checkIfDone return v - runIter i (EOF Nothing) - (EOF e) -> return $ Cont undefined e - (Chunk (WrapBS x)) -> do - let n = S.length x - if n == 0 - then do - i' <- wrap iter - return $ Cont i' Nothing - else do - let o = S.concat [ toHex (toEnum n) - , "\r\n" - , x - , "\r\n" ] - v <- runIter iter (Chunk $ WrapBS o) - i <- checkIfDone wrap v - return $ Cont i Nothing - - -chunkParserToEnumerator :: (Monad m) => - Iteratee m (Maybe ByteString) - -> Iteratee m a - -> m (Iteratee m a) -chunkParserToEnumerator getChunk client = return $ do - mbB <- getChunk - maybe (finishIt client) (sendBS client) mbB - - where - sendBS iter s = do - v <- lift $ runIter iter (Chunk $ toWrap $ L.fromChunks [s]) - - case v of - (Done _ (EOF (Just e))) -> throwErr e - - (Done x _) -> return x - - (Cont _ (Just e)) -> throwErr e - - (Cont k Nothing) -> joinIM $ - chunkParserToEnumerator getChunk k - - finishIt iter = do - e <- lift $ sendEof iter - - case e of - Left x -> throwErr x - Right x -> return x - - sendEof iter = do - v <- runIter iter (EOF Nothing) - - return $ case v of - (Done _ (EOF (Just e))) -> Left e - (Done x _) -> Right x - (Cont _ (Just e)) -> Left e - (Cont _ _) -> Left $ Err $ "divergent iteratee" - - ------------------------------------------------------------------------------- --- parse functions ------------------------------------------------------------------------------- - --- theft alert: many of these routines adapted from Johan Tibell's hyena --- package - --- | Parsers for different tokens in an HTTP request. -sp, digit, letter :: Parser Word8 -sp = word8 $ c2w ' ' -digit = satisfy (isDigit . w2c) -letter = satisfy (isAlpha . w2c) - -untilEOL :: Parser ByteString -untilEOL = takeWhile notend - where - notend d = let c = w2c d in not $ c == '\r' || c == '\n' - -crlf :: Parser ByteString -crlf = string "\r\n" - --- | Parser for zero or more spaces. -spaces :: Parser [Word8] -spaces = many sp - -pSpaces :: Parser ByteString -pSpaces = takeWhile (isSpace . w2c) - --- | Parser for the internal request data type. -pRequest :: Parser (Maybe IRequest) -pRequest = (Just <$> pRequest') <|> (endOfInput *> pure Nothing) - -pRequest' :: Parser IRequest -pRequest' = IRequest - <$> (option "" crlf *> pMethod) <* sp - <*> pUri <* sp - <*> pVersion <* crlf - <*> pHeaders <* crlf - - -- note: the optional crlf is at the beginning because some older browsers - -- send an extra crlf after a POST body - - --- | Parser for the request method. -pMethod :: Parser Method -pMethod = (OPTIONS <$ string "OPTIONS") - <|> (GET <$ string "GET") - <|> (HEAD <$ string "HEAD") - <|> word8 (c2w 'P') *> ((POST <$ string "OST") <|> - (PUT <$ string "UT")) - <|> (DELETE <$ string "DELETE") - <|> (TRACE <$ string "TRACE") - <|> (CONNECT <$ string "CONNECT") - --- | Parser for the request URI. -pUri :: Parser ByteString -pUri = takeWhile (not . isSpace . w2c) - --- | Parser for the request's HTTP protocol version. -pVersion :: Parser (Int, Int) -pVersion = string "HTTP/" *> - liftA2 (,) (digit' <* word8 (c2w '.')) digit' - where - digit' = fmap (digitToInt . w2c) digit - -fieldChars :: Parser ByteString -fieldChars = takeWhile isFieldChar - where - isFieldChar c = (Vec.!) fieldCharTable (fromEnum c) - -fieldCharTable :: Vector Bool -fieldCharTable = Vec.generate 256 f - where - f d = let c=toEnum d in (isDigit c) || (isAlpha c) || c == '-' || c == '_' - - --- | Parser for request headers. -pHeaders :: Parser [(ByteString, ByteString)] -pHeaders = many header - where - header = {-# SCC "pHeaders/header" #-} - liftA2 (,) - fieldName - (word8 (c2w ':') *> spaces *> contents) - - fieldName = {-# SCC "pHeaders/fieldName" #-} - liftA2 S.cons letter fieldChars - - contents = {-# SCC "pHeaders/contents" #-} - liftA2 S.append - (untilEOL <* crlf) - (continuation <|> pure S.empty) - - isLeadingWS w = {-# SCC "pHeaders/isLeadingWS" #-} - elem w wstab - - wstab = map c2w " \t" - - leadingWhiteSpace = {-# SCC "pHeaders/leadingWhiteSpace" #-} - takeWhile1 isLeadingWS - - continuation = {-# SCC "pHeaders/continuation" #-} - liftA2 S.cons - (leadingWhiteSpace *> pure (c2w ' ')) - contents - - -pGetTransferChunk :: Parser (Maybe ByteString) -pGetTransferChunk = do - !hex <- liftM fromHex $ (takeWhile (isHexDigit . w2c)) - takeTill ((== '\r') . w2c) - crlf - if hex <= 0 - then return Nothing - else do - x <- take hex - crlf - return $ Just x - where - fromHex :: ByteString -> Int - fromHex s = Cvt.hex (L.fromChunks [s]) - - ------------------------------------------------------------------------------- --- COOKIE PARSING ------------------------------------------------------------------------------- - --- these definitions try to mirror RFC-2068 (the HTTP/1.1 spec) and RFC-2109 --- (cookie spec): please point out any errors! - -{-# INLINE matchAll #-} -matchAll :: [ Char -> Bool ] -> Char -> Bool -matchAll x c = and $ map ($ c) x - -{-# INLINE isToken #-} -isToken :: Char -> Bool -isToken c = (Vec.!) tokenTable (fromEnum c) - where - tokenTable :: Vector Bool - tokenTable = Vec.generate 256 (f . toEnum) - - f = matchAll [ isAscii - , not . isControl - , not . isSpace - , not . flip elem [ '(', ')', '<', '>', '@', ',', ';' - , ':', '\\', '\"', '/', '[', ']' - , '?', '=', '{', '}' ] - ] - -{-# INLINE isRFCText #-} -isRFCText :: Char -> Bool -isRFCText = not . isControl - -pToken :: Parser ByteString -pToken = takeWhile (isToken . w2c) - - -pQuotedString :: Parser ByteString -pQuotedString = q *> quotedText <* q - where - quotedText = (S.concat . reverse) <$> f [] - - f soFar = do - t <- takeWhile qdtext - - let soFar' = t:soFar - - -- RFC says that backslash only escapes for <"> - choice [ string "\\\"" *> f ("\"" : soFar') - , pure soFar' ] - - - q = word8 $ c2w '\"' - - qdtext = matchAll [ isRFCText, (/= '\"'), (/= '\\') ] . w2c - - -pCookies :: Parser [Cookie] -pCookies = do - -- grab kvps and turn to strict bytestrings - kvps <- pAvPairs - - return $ map toCookie $ filter (not . S.isPrefixOf "$" . fst) kvps - - where - toCookie (nm,val) = Cookie nm val Nothing Nothing Nothing - - --- unhelpfully, the spec mentions "old-style" cookies that don't have quotes --- around the value. wonderful. -pWord :: Parser ByteString -pWord = pQuotedString <|> (takeWhile ((/= ';') . w2c)) - -pAvPairs :: Parser [(ByteString, ByteString)] -pAvPairs = do - a <- pAvPair - b <- many (pSpaces *> char ';' *> pSpaces *> pAvPair) - - return $ a:b - -pAvPair :: Parser (ByteString, ByteString) -pAvPair = do - key <- pToken <* pSpaces - val <- option "" $ char '=' *> pSpaces *> pWord - - return (key,val) - -parseCookie :: ByteString -> Maybe [Cookie] -parseCookie = parseToCompletion pCookies - ------------------------------------------------------------------------------- --- MULTIPART/FORMDATA ------------------------------------------------------------------------------- - -parseUrlEncoded :: ByteString -> Map ByteString [ByteString] -parseUrlEncoded s = foldl' (\m (k,v) -> Map.insertWith' (++) k [v] m) - Map.empty - decoded - where - breakApart = (second (S.drop 1)) . S.break (== (c2w '=')) - - parts :: [(ByteString,ByteString)] - parts = map breakApart $ S.split (c2w '&') s - - urldecode = parseToCompletion pUrlEscaped - - decodeOne (a,b) = do - a' <- urldecode a - b' <- urldecode b - return (a',b') - - decoded = catMaybes $ map decodeOne parts - - ------------------------------------------------------------------------------- --- utility functions ------------------------------------------------------------------------------- - -strictize :: L.ByteString -> ByteString -strictize = S.concat . L.toChunks - ------------------------------------------------------------------------------- -char :: Char -> Parser Word8 -char = word8 . c2w - -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-Date.html b/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-Date.html deleted file mode 100644 index 07f8c6f..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-Date.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} - -module Snap.Internal.Http.Server.Date -( getDateString -, getLogDateString -, getCurrentDateTime) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString (ByteString) -import Data.IORef -import Foreign.C.Types -import System.IO.Unsafe - -#ifndef PORTABLE -import System.Posix.Time -#else -import Data.Time.Clock.POSIX -#endif - -import Snap.Internal.Http.Types (formatHttpTime, formatLogTime) - --- Here comes a dirty hack. We don't want to be wasting context switches --- building date strings, so we're only going to compute one every two --- seconds. (Approximate timestamps to within a couple of seconds are OK here, --- and we'll reduce overhead.) --- --- Note that we also don't want to wake up a potentially sleeping CPU by just --- running the computation on a timer. We'll allow client traffic to trigger --- the process. - -data DateState = DateState { - _cachedDateString :: !(IORef ByteString) - , _cachedLogString :: !(IORef ByteString) - , _cachedDate :: !(IORef CTime) - , _valueIsOld :: !(IORef Bool) - , _morePlease :: !(MVar ()) - , _dataAvailable :: !(MVar ()) - , _dateThread :: !(MVar ThreadId) - } - -dateState :: DateState -dateState = unsafePerformIO $ do - (s1,s2,date) <- fetchTime - bs1 <- newIORef s1 - bs2 <- newIORef s2 - dt <- newIORef date - ov <- newIORef False - th <- newEmptyMVar - mp <- newMVar () - da <- newMVar () - - let d = DateState bs1 bs2 dt ov mp da th - - t <- forkIO $ dateThread d - putMVar th t - - return d - - -#ifdef PORTABLE -epochTime :: IO CTime -epochTime = do - t <- getPOSIXTime - return $ fromInteger $ truncate t -#endif - - -fetchTime :: IO (ByteString,ByteString,CTime) -fetchTime = do - now <- epochTime - t1 <- formatHttpTime now - t2 <- formatLogTime now - return (t1, t2, now) - - -dateThread :: DateState -> IO () -dateThread ds@(DateState dateString logString time valueIsOld morePlease - dataAvailable _) = do - -- a lot of effort to make sure we don't deadlock - takeMVar morePlease - - (s1,s2,now) <- fetchTime - atomicModifyIORef dateString $ const (s1,()) - atomicModifyIORef logString $ const (s2,()) - atomicModifyIORef time $ const (now,()) - - writeIORef valueIsOld False - tryPutMVar dataAvailable () - - threadDelay 2000000 - - takeMVar dataAvailable - writeIORef valueIsOld True - - dateThread ds - -ensureFreshDate :: IO () -ensureFreshDate = block $ do - old <- readIORef $ _valueIsOld dateState - when old $ do - tryPutMVar (_morePlease dateState) () - readMVar $ _dataAvailable dateState - -getDateString :: IO ByteString -getDateString = block $ do - ensureFreshDate - readIORef $ _cachedDateString dateState - - -getLogDateString :: IO ByteString -getLogDateString = block $ do - ensureFreshDate - readIORef $ _cachedLogString dateState - - -getCurrentDateTime :: IO CTime -getCurrentDateTime = block $ do - ensureFreshDate - readIORef $ _cachedDate dateState - -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html b/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html deleted file mode 100644 index 94aeee4..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server-LibevBackend.html +++ /dev/null @@ -1,745 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server.LibevBackend - ( Backend - , BackendTerminatedException - , Connection - , TimeoutException - , name - , debug - , bindIt - , new - , stop - , withConnection - , sendFile - , tickleTimeout - , getReadEnd - , getWriteEnd - , getRemoteAddr - , getRemotePort - , getLocalAddr - , getLocalPort - ) where - ---------------------------- --- TODO: document module -- ---------------------------- - ------------------------------------------------------------------------------- -import Control.Concurrent -import Control.Exception -import Control.Monad -import Control.Monad.Trans -import Data.ByteString (ByteString) -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Unsafe as B -import qualified Data.ByteString as B -import Data.IORef -import Data.Iteratee.WrappedByteString -import Data.Set (Set) -import qualified Data.Set as Set -import Data.Typeable -import Foreign hiding (new) -import Foreign.C.Error -import Foreign.C.Types -import GHC.Conc (forkOnIO) -import Network.Libev -import Network.Socket -import qualified Network.Socket.SendFile as SF -import Prelude hiding (catch) -import System.Timeout ------------------------------------------------------------------------------- -import Snap.Iteratee -import Snap.Internal.Debug - - -data Backend = Backend - { _acceptSocket :: Socket - , _acceptFd :: CInt - , _connectionQueue :: Chan CInt - , _evLoop :: EvLoopPtr - , _acceptIOCallback :: FunPtr IoCallback - , _acceptIOObj :: EvIoPtr - - -- FIXME: we don't need _loopThread - , _loopThread :: MVar ThreadId - , _mutexCallbacks :: (FunPtr MutexCallback, FunPtr MutexCallback) - , _loopLock :: MVar () - , _asyncCb :: FunPtr AsyncCallback - , _asyncObj :: EvAsyncPtr - , _killCb :: FunPtr AsyncCallback - , _killObj :: EvAsyncPtr - , _connectionThreads :: MVar (Set ThreadId) - , _backendCPU :: Int - } - - -data Connection = Connection - { _backend :: !Backend - , _socket :: !Socket - , _socketFd :: !CInt - , _remoteAddr :: !ByteString - , _remotePort :: !Int - , _localAddr :: !ByteString - , _localPort :: !Int - , _readAvailable :: !(MVar ()) - , _writeAvailable :: !(MVar ()) - , _timerObj :: !EvTimerPtr - , _timerCallback :: !(FunPtr TimerCallback) - , _readActive :: !(IORef Bool) - , _writeActive :: !(IORef Bool) - , _connReadIOObj :: !EvIoPtr - , _connReadIOCallback :: !(FunPtr IoCallback) - , _connWriteIOObj :: !EvIoPtr - , _connWriteIOCallback :: !(FunPtr IoCallback) - , _connThread :: !(MVar ThreadId) - } - -{-# INLINE name #-} -name :: ByteString -name = "libev" - - -sendFile :: Connection -> FilePath -> IO () -sendFile c fp = do - withMVar lock $ \_ -> do - act <- readIORef $ _writeActive c - when act $ evIoStop loop io - - SF.sendFile s fp - - withMVar lock $ \_ -> do - tryPutMVar (_readAvailable c) () - tryPutMVar (_writeAvailable c) () - evIoStart loop io - writeIORef (_writeActive c) True - evAsyncSend loop asy - - where - s = _socket c - io = _connWriteIOObj c - b = _backend c - loop = _evLoop b - lock = _loopLock b - asy = _asyncObj b - - -bindIt :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> IO (Socket,CInt) -bindIt bindAddress bindPort = do - sock <- socket AF_INET Stream 0 - addr <- getHostAddr bindPort bindAddress - setSocketOption sock ReuseAddr 1 - bindSocket sock addr - listen sock 150 - let sockFd = fdSocket sock - c_setnonblocking sockFd - return (sock, sockFd) - - -new :: (Socket,CInt) -- ^ value you got from bindIt - -> Int -- ^ cpu - -> IO Backend -new (sock,sockFd) cpu = do - connq <- newChan - - -- We'll try kqueue on OSX even though the libev docs complain that it's - -- "broken", in the hope that it works as expected for sockets - f <- evRecommendedBackends - lp <- evLoopNew $ toEnum . fromEnum $ f .|. evbackend_kqueue - - - -- we'll be working multithreaded so we need to set up locking for the C - -- event loop struct - (mc1,mc2,looplock) <- setupLockingForLoop lp - - -- setup async callbacks -- these allow us to wake up the main loop - -- (normally blocked in c-land) from other threads - asyncObj <- mkEvAsync - asyncCB <- mkAsyncCallback $ \_ _ _ -> do - debug "async wakeup" - return () - - killObj <- mkEvAsync - killCB <- mkAsyncCallback $ \_ _ _ -> do - debug "async kill wakeup" - evUnloop lp 2 - return () - - evAsyncInit asyncObj asyncCB - evAsyncStart lp asyncObj - evAsyncInit killObj killCB - evAsyncStart lp killObj - - -- setup the accept callback; this watches for read readiness on the listen - -- port - accCB <- mkIoCallback $ acceptCallback sockFd connq - accIO <- mkEvIo - evIoInit accIO accCB sockFd ev_read - evIoStart lp accIO - - -- an MVar for the loop thread, and one to keep track of the set of active - -- threads - threadMVar <- newEmptyMVar - threadSetMVar <- newMVar Set.empty - - let b = Backend sock - sockFd - connq - lp - accCB - accIO - threadMVar - (mc1,mc2) - looplock - asyncCB - asyncObj - killCB - killObj - threadSetMVar - cpu - - tid <- forkOnIO cpu $ loopThread b - putMVar threadMVar tid - - debug $ "Backend.new: loop spawned" - return b - - --- | Run evLoop in a thread -loopThread :: Backend -> IO () -loopThread backend = do - debug $ "starting loop" - (ignoreException go) `finally` cleanup - debug $ "loop finished" - where - cleanup = do - debug $ "loopThread: cleaning up" - ignoreException $ freeBackend backend - lock = _loopLock backend - loop = _evLoop backend - go = takeMVar lock >> block (evLoop loop 0) - - -acceptCallback :: CInt -> Chan CInt -> IoCallback -acceptCallback accFd chan _loopPtr _ioPtr _ = do - debug "inside acceptCallback" - r <- c_accept accFd - - case r of - -- this (EWOULDBLOCK) shouldn't happen (we just got told it was ready!), - -- if it does (maybe the request got picked up by another thread) we'll - -- just bail out - -2 -> return () - -1 -> debugErrno "Backend.acceptCallback:c_accept()" - fd -> do - debug $ "acceptCallback: accept()ed fd, writing to chan " ++ show fd - writeChan chan fd - - -ioReadCallback :: CInt -> IORef Bool -> MVar () -> IoCallback -ioReadCallback fd active ra _loopPtr _ioPtr _ = do - -- send notifications to the worker thread - debug $ "ioReadCallback: notification (" ++ show fd ++ ")" - tryPutMVar ra () - debug $ "stopping ioReadCallback (" ++ show fd ++ ")" - evIoStop _loopPtr _ioPtr - writeIORef active False - - -ioWriteCallback :: CInt -> IORef Bool -> MVar () -> IoCallback -ioWriteCallback fd active wa _loopPtr _ioPtr _ = do - -- send notifications to the worker thread - debug $ "ioWriteCallback: notification (" ++ show fd ++ ")" - tryPutMVar wa () - debug $ "stopping ioWriteCallback (" ++ show fd ++ ")" - evIoStop _loopPtr _ioPtr - writeIORef active False - - -seconds :: Int -> Int -seconds n = n * ((10::Int)^(6::Int)) - - -stop :: Backend -> IO () -stop b = ignoreException $ do - debug $ "Backend.stop" - - -- FIXME: what are we gonna do here? - -- - -- 1. take the loop lock - -- 2. shut down the accept() callback - -- 3. stuff a poison pill (a bunch of -1 values should do) down the - -- connection queue so that withConnection knows to throw an exception - -- back up to its caller - -- 4. release the loop lock - -- 5. wait until all of the threads have finished, or until 10 seconds have - -- elapsed, whichever comes first - -- 6. take the loop lock - -- 7. call evUnloop and wake up the loop using evAsyncSend - -- 8. release the loop lock, the main loop thread should then free/clean - -- everything up (threads, connections, io objects, callbacks, etc) - - withMVar lock $ \_ -> do - evIoStop loop acceptObj - replicateM_ 10 $ writeChan connQ (-1) - - debug $ "Backend.stop: waiting at most 10 seconds for connection threads to die" - waitForThreads b $ seconds 10 - debug $ "Backend.stop: all threads dead, unlooping" - - withMVar lock $ \_ -> do - -- FIXME: hlibev should export EVUNLOOP_ALL - evUnloop loop 2 - evAsyncSend loop killObj - - debug $ "unloop sent" - - - where - loop = _evLoop b - acceptObj = _acceptIOObj b - killObj = _killObj b - lock = _loopLock b - connQ = _connectionQueue b - - - -waitForThreads :: Backend -> Int -> IO () -waitForThreads backend t = timeout t wait >> return () - where - threadSet = _connectionThreads backend - wait = do - threads <- readMVar threadSet - if (Set.null threads) - then return () - else threadDelay (seconds 1) >> wait - - - -getAddr :: SockAddr -> IO (ByteString, Int) -getAddr addr = - case addr of - SockAddrInet p ha -> do - s <- liftM (B.pack . map c2w) (inet_ntoa ha) - return (s, fromIntegral p) - - a -> throwIO $ AddressNotSupportedException (show a) - - --- | throw a timeout exception to the handling thread -- it'll clean up --- everything -timerCallback :: MVar ThreadId -> TimerCallback -timerCallback tmv _ _ _ = do - debug "timer callback" - tid <- readMVar tmv - throwTo tid TimeoutException - - -freeConnection :: Connection -> IO () -freeConnection conn = ignoreException $ do - withMVar loopLock $ \_ -> block $ do - debug $ "freeConnection (" ++ show fd ++ ")" - c_close fd - - -- stop and free timer object - evTimerStop loop timerObj - freeEvTimer timerObj - freeTimerCallback timerCb - - -- stop and free i/o objects - evIoStop loop ioWrObj - freeEvIo ioWrObj - freeIoCallback ioWrCb - - evIoStop loop ioRdObj - freeEvIo ioRdObj - freeIoCallback ioRdCb - - -- remove the thread id from the backend set - tid <- readMVar threadMVar - modifyMVar_ tsetMVar $ \s -> do - let !s' = Set.delete tid s - return $! s' - - -- wake up the event loop so it can be apprised of the changes - evAsyncSend loop asyncObj - - where - backend = _backend conn - tsetMVar = _connectionThreads backend - loop = _evLoop backend - loopLock = _loopLock backend - asyncObj = _asyncObj backend - - fd = _socketFd conn - threadMVar = _connThread conn - ioWrObj = _connWriteIOObj conn - ioWrCb = _connWriteIOCallback conn - ioRdObj = _connReadIOObj conn - ioRdCb = _connReadIOCallback conn - timerObj = _timerObj conn - timerCb = _timerCallback conn - - -ignoreException :: IO () -> IO () -ignoreException = handle (\(_::SomeException) -> return ()) - - -freeBackend :: Backend -> IO () -freeBackend backend = ignoreException $ block $ do - -- note: we only get here after an unloop - - withMVar tsetMVar $ \set -> do - mapM_ killThread $ Set.toList set - - debug $ "Backend.freeBackend: wait at most 2 seconds for threads to die" - waitForThreads backend $ seconds 2 - - debug $ "Backend.freeBackend: all threads dead" - - debug $ "Backend.freeBackend: destroying resources" - freeEvIo acceptObj - freeIoCallback acceptCb - c_close fd - - evAsyncStop loop asyncObj - freeEvAsync asyncObj - freeAsyncCallback asyncCb - - evAsyncStop loop killObj - freeEvAsync killObj - freeAsyncCallback killCb - - freeMutexCallback mcb1 - freeMutexCallback mcb2 - - evLoopDestroy loop - debug $ "Backend.freeBackend: resources destroyed" - - where - fd = _acceptFd backend - acceptObj = _acceptIOObj backend - acceptCb = _acceptIOCallback backend - tsetMVar = _connectionThreads backend - asyncObj = _asyncObj backend - asyncCb = _asyncCb backend - killObj = _killObj backend - killCb = _killCb backend - (mcb1,mcb2) = _mutexCallbacks backend - loop = _evLoop backend - - --- | Note: proc gets run in the background -withConnection :: Backend -> Int -> (Connection -> IO ()) -> IO () -withConnection backend cpu proc = go - where - threadProc conn = ignoreException (proc conn) `finally` freeConnection conn - - go = do - debug $ "withConnection: reading from chan" - fd <- readChan $ _connectionQueue backend - debug $ "withConnection: got fd " ++ show fd - - -- if fd < 0 throw an exception here (because this only happens if stop - -- is called) - when (fd < 0) $ throwIO BackendTerminatedException - - sock <- mkSocket fd AF_INET Stream 0 Connected - peerName <- getPeerName sock - sockName <- getSocketName sock - - -- set_linger fd - c_setnonblocking fd - - (remoteAddr, remotePort) <- getAddr peerName - (localAddr, localPort) <- getAddr sockName - - let lp = _evLoop backend - - -- makes sense to assume the socket is read/write available when - -- opened; worst-case is we get EWOULDBLOCK - ra <- newMVar () - wa <- newMVar () - - tmr <- mkEvTimer - thrmv <- newEmptyMVar - tcb <- mkTimerCallback $ timerCallback thrmv - evTimerInit tmr tcb 0 20.0 - - readActive <- newIORef True - writeActive <- newIORef True - - evioRead <- mkEvIo - ioReadCb <- mkIoCallback $ ioReadCallback fd readActive ra - - evioWrite <- mkEvIo - ioWriteCb <- mkIoCallback $ ioWriteCallback fd writeActive wa - - evIoInit evioRead ioReadCb fd ev_read - evIoInit evioWrite ioWriteCb fd ev_write - - -- take ev_loop lock, start timer and io watchers - withMVar (_loopLock backend) $ \_ -> do - evTimerAgain lp tmr - evIoStart lp evioRead - evIoStart lp evioWrite - - -- wakeup the loop thread so that these new watchers get - -- registered next time through the loop - evAsyncSend lp $ _asyncObj backend - - let conn = Connection backend - sock - fd - remoteAddr - remotePort - localAddr - localPort - ra - wa - tmr - tcb - readActive - writeActive - evioRead - ioReadCb - evioWrite - ioWriteCb - thrmv - - - tid <- forkOnIO cpu $ threadProc conn - - modifyMVar_ (_connectionThreads backend) $ ins tid - putMVar thrmv tid - - where - ins !thr !s = let !r = Set.insert thr s in return (r `seq` r) - - -data BackendTerminatedException = BackendTerminatedException - deriving (Typeable) - -instance Show BackendTerminatedException where - show BackendTerminatedException = "Backend terminated" - -instance Exception BackendTerminatedException - - - -data AddressNotSupportedException = AddressNotSupportedException String - deriving (Typeable) - -instance Show AddressNotSupportedException where - show (AddressNotSupportedException x) = "Address not supported: " ++ x - -instance Exception AddressNotSupportedException - - -getRemoteAddr :: Connection -> ByteString -getRemoteAddr = _remoteAddr - -getRemotePort :: Connection -> Int -getRemotePort = _remotePort - -getLocalAddr :: Connection -> ByteString -getLocalAddr = _localAddr - -getLocalPort :: Connection -> Int -getLocalPort = _localPort - ------------------------------------------------------------------------------- - --- fixme: new function name -getHostAddr :: Int - -> ByteString - -> IO SockAddr -getHostAddr p s = do - h <- if s == "*" - then return iNADDR_ANY - else inet_addr (map w2c . B.unpack $ s) - - return $ SockAddrInet (fromIntegral p) h - - - -bLOCKSIZE :: Int -bLOCKSIZE = 8192 - --- --- About timeouts --- --- It's not good enough to restart the timer from io(Read|Write)Callback, --- because those seem to be edge-triggered. I've definitely had where after --- 20 seconds they still weren't being re-awakened. --- - -data TimeoutException = TimeoutException - deriving (Typeable) - -instance Show TimeoutException where - show _ = "timeout" - -instance Exception TimeoutException - -tickleTimeout :: Connection -> IO () -tickleTimeout conn = debug "Backend.tickleTimeout" >> evTimerAgain lp tmr - where - bk = _backend conn - lp = _evLoop bk - tmr = _timerObj conn - -recvData :: Connection -> Int -> IO ByteString -recvData conn n = do - dbg "entered" - allocaBytes n $ \cstr -> do - sz <- throwErrnoIfMinus1RetryMayBlock - "recvData" - (c_read fd cstr (toEnum n)) - waitForLock - - -- we got activity, but don't do restart timer due to the - -- slowloris attack - - dbg $ "sz returned " ++ show sz - - if sz == 0 - then return "" - else B.packCStringLen ((castPtr cstr),(fromEnum sz)) - - where - io = _connReadIOObj conn - bk = _backend conn - active = _readActive conn - lp = _evLoop bk - looplock = _loopLock bk - async = _asyncObj bk - - dbg s = debug $ "Backend.recvData(" ++ show (_socketFd conn) ++ "): " ++ s - - fd = _socketFd conn - lock = _readAvailable conn - waitForLock = do - dbg "start waitForLock" - - withMVar looplock $ \_ -> do - act <- readIORef active - if act - then dbg "read watcher already active, skipping" - else do - dbg "starting watcher, sending async" - evIoStart lp io - writeIORef active True - evAsyncSend lp async - - dbg "waitForLock: waiting for mvar" - takeMVar lock - dbg "waitForLock: took mvar" - - -sendData :: Connection -> ByteString -> IO () -sendData conn bs = do - let len = B.length bs - dbg $ "entered w/ " ++ show len ++ " bytes" - written <- B.unsafeUseAsCString bs $ \cstr -> - throwErrnoIfMinus1RetryMayBlock - "sendData" - (c_write fd cstr (toEnum len)) - waitForLock - - -- we got activity, so restart timer - tickleTimeout conn - - let n = fromEnum written - let last10 = B.drop (n-10) $ B.take n bs - - dbg $ "wrote " ++ show written ++ " bytes, last 10='" ++ show last10 ++ "'" - - if n < len - then do - dbg $ "short write, need to write " ++ show (len-n) ++ " more bytes" - sendData conn $ B.drop n bs - else return () - - where - io = _connWriteIOObj conn - bk = _backend conn - active = _writeActive conn - lp = _evLoop bk - looplock = _loopLock bk - async = _asyncObj bk - - dbg s = debug $ "Backend.sendData(" ++ show (_socketFd conn) ++ "): " ++ s - fd = _socketFd conn - lock = _writeAvailable conn - waitForLock = do - dbg "waitForLock: starting" - withMVar looplock $ \_ -> do - act <- readIORef active - if act - then dbg "write watcher already running, skipping" - else do - dbg "starting watcher, sending async event" - evIoStart lp io - writeIORef active True - evAsyncSend lp async - - dbg "waitForLock: taking mvar" - takeMVar lock - dbg "waitForLock: took mvar" - - -getReadEnd :: Connection -> Enumerator IO a -getReadEnd = enumerate - - -getWriteEnd :: Connection -> Iteratee IO () -getWriteEnd = writeOut - - -enumerate :: (MonadIO m) => Connection -> Enumerator m a -enumerate = loop - where - loop conn f = do - s <- liftIO $ recvData conn bLOCKSIZE - sendOne conn f s - - sendOne conn f s = do - v <- runIter f (if B.null s - then EOF Nothing - else Chunk $ WrapBS s) - case v of - r@(Done _ _) -> return $ liftI r - (Cont k Nothing) -> loop conn k - (Cont _ (Just e)) -> return $ throwErr e - - -writeOut :: (MonadIO m) => Connection -> Iteratee m () -writeOut conn = IterateeG out - where - out c@(EOF _) = return $ Done () c - - out (Chunk s) = do - let x = unWrap s - - liftIO $ sendData conn x - - return $ Cont (writeOut conn) Nothing - -- diff --git a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server.html b/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server.html deleted file mode 100644 index 075db14..0000000 --- a/static/docs/0.2.4/snap-server/src/Snap-Internal-Http-Server.html +++ /dev/null @@ -1,676 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Snap.Internal.Http.Server where - ------------------------------------------------------------------------------- -import Control.Arrow (first, second) -import Control.Monad.State.Strict -import Control.Concurrent.MVar -import Control.Exception -import Data.Char -import Data.CIByteString -import Data.ByteString (ByteString) -import qualified Data.ByteString as S -import qualified Data.ByteString.Char8 as SC -import qualified Data.ByteString.Lazy as L -import Data.ByteString.Internal (c2w, w2c) -import qualified Data.ByteString.Nums.Careless.Int as Cvt -import Data.IORef -import Data.List (foldl') -import qualified Data.Map as Map -import Data.Maybe (fromJust, catMaybes, fromMaybe) -import Data.Monoid -import Data.Version -import Foreign.C.Types -import Foreign.ForeignPtr -import GHC.Conc -import Prelude hiding (catch, show, Show) -import qualified Prelude -import System.PosixCompat.Files hiding (setFileSize) -import System.Posix.Types (FileOffset) -import Text.Show.ByteString hiding (runPut) ------------------------------------------------------------------------------- -import System.FastLogger -import Snap.Internal.Http.Types hiding (Enumerator) -import Snap.Internal.Http.Parser -import Snap.Iteratee hiding (foldl', head, take, FileOffset) -import qualified Snap.Iteratee as I - -#ifdef LIBEV -import qualified Snap.Internal.Http.Server.LibevBackend as Backend -import Snap.Internal.Http.Server.LibevBackend (debug) -#else -import qualified Snap.Internal.Http.Server.SimpleBackend as Backend -import Snap.Internal.Http.Server.SimpleBackend (debug) -#endif - -import Snap.Internal.Http.Server.Date - -import qualified Paths_snap_server as V - ------------------------------------------------------------------------------- --- | The handler has to return the request object because we have to clear the --- HTTP request body before we send the response. If the handler consumes the --- request body, it is responsible for setting @rqBody=return@ in the returned --- request (otherwise we will mess up reading the input stream). --- --- Note that we won't be bothering end users with this -- the details will be --- hidden inside the Snap monad -type ServerHandler = (ByteString -> IO ()) - -> Request - -> Iteratee IO (Request,Response) - -type ServerMonad = StateT ServerState (Iteratee IO) - -data ServerState = ServerState - { _forceConnectionClose :: Bool - , _localHostname :: ByteString - , _localAddress :: ByteString - , _localPort :: Int - , _remoteAddr :: ByteString - , _remotePort :: Int - , _logAccess :: Request -> Response -> IO () - , _logError :: ByteString -> IO () - } - - ------------------------------------------------------------------------------- -runServerMonad :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> (Request -> Response -> IO ()) -- ^ access log function - -> (ByteString -> IO ()) -- ^ error log function - -> ServerMonad a -- ^ monadic action to run - -> Iteratee IO a -runServerMonad lh lip lp rip rp la le m = evalStateT m st - where - st = ServerState False lh lip lp rip rp la le - - - ------------------------------------------------------------------------------- --- input/output - - ------------------------------------------------------------------------------- -httpServe :: ByteString -- ^ bind address, or \"*\" for all - -> Int -- ^ port to bind to - -> ByteString -- ^ local hostname (server name) - -> Maybe FilePath -- ^ path to the access log - -> Maybe FilePath -- ^ path to the error log - -> ServerHandler -- ^ handler procedure - -> IO () -httpServe bindAddress bindPort localHostname alogPath elogPath handler = - withLoggers alogPath elogPath - (\(alog, elog) -> spawnAll alog elog) - - where - spawnAll alog elog = {-# SCC "httpServe/spawnAll" #-} do - logE elog $ S.concat [ "Server.httpServe: START (" - , Backend.name, ")"] - let n = numCapabilities - bracket (spawn n) - (\xs -> do - logE elog "Server.httpServe: SHUTDOWN" - mapM_ (Backend.stop . fst) xs - logE elog "Server.httpServe: BACKEND STOPPED") - (runAll alog elog) - - - runAll alog elog xs = {-# SCC "httpServe/runAll" #-} do - mapM_ f $ xs `zip` [0..] - mapM_ (takeMVar . snd) xs - where - f ((backend,mvar),cpu) = - forkOnIO cpu $ do - labelMe $ map w2c $ S.unpack $ - S.concat ["accThread ", l2s $ show cpu] - (try $ (goooo alog elog backend cpu)) :: IO (Either SomeException ()) - putMVar mvar () - - goooo alog elog backend cpu = - {-# SCC "httpServe/goooo" #-} - let loop = go alog elog backend cpu >> loop - in loop - - maybeSpawnLogger = maybe (return Nothing) $ (liftM Just) . newLogger - - withLoggers afp efp = - bracket (do alog <- maybeSpawnLogger afp - elog <- maybeSpawnLogger efp - return (alog, elog)) - (\(alog, elog) -> do - threadDelay 1000000 - maybe (return ()) stopLogger alog - maybe (return ()) stopLogger elog) - - labelMe :: String -> IO () - labelMe s = do - tid <- myThreadId - labelThread tid s - - spawn n = do - sock <- Backend.bindIt bindAddress bindPort - backends <- mapM (Backend.new sock) $ [0..(n-1)] - mvars <- replicateM n newEmptyMVar - - return (backends `zip` mvars) - - - runOne alog elog backend cpu = - Backend.withConnection backend cpu $ \conn -> - {-# SCC "httpServe/runOne" #-} do - debug "Server.httpServe.runOne: entered" - let readEnd = Backend.getReadEnd conn - let writeEnd = Backend.getWriteEnd conn - - let raddr = Backend.getRemoteAddr conn - let rport = Backend.getRemotePort conn - let laddr = Backend.getLocalAddr conn - let lport = Backend.getLocalPort conn - - runHTTP localHostname laddr lport raddr rport - alog elog readEnd writeEnd - (Backend.sendFile conn) - (Backend.tickleTimeout conn) handler - - debug "Server.httpServe.runHTTP: finished" - - - go alog elog backend cpu = runOne alog elog backend cpu - `catches` - [ Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: AsyncException) -> do - logE elog $ - S.concat [ "Server.httpServe.go: got async exception, " - , "terminating:\n", bshow e ] - throwIO e - - , Handler $ \(e :: Backend.BackendTerminatedException) -> do - logE elog $ "Server.httpServe.go: got backend terminated, waiting for cleanup" - throwIO e - - , Handler $ \(e :: IOException) -> do - logE elog $ S.concat [ "Server.httpServe.go: got io exception: " - , bshow e ] - - , Handler $ \(e :: SomeException) -> do - logE elog $ S.concat [ "Server.httpServe.go: got someexception: " - , bshow e ] ] - ------------------------------------------------------------------------------- -debugE :: (MonadIO m) => ByteString -> m () -debugE s = debug $ "Server: " ++ (map w2c $ S.unpack s) - - ------------------------------------------------------------------------------- -logE :: Maybe Logger -> ByteString -> IO () -logE elog = maybe debugE (\l s -> debugE s >> logE' l s) elog - -logE' :: Logger -> ByteString -> IO () -logE' logger s = (timestampedLogEntry s) >>= logMsg logger - - -bshow :: (Prelude.Show a) => a -> ByteString -bshow = toBS . Prelude.show - ------------------------------------------------------------------------------- -logA ::Maybe Logger -> Request -> Response -> IO () -logA alog = maybe (\_ _ -> return ()) logA' alog - -logA' :: Logger -> Request -> Response -> IO () -logA' logger req rsp = do - let hdrs = rqHeaders req - let host = rqRemoteAddr req - let user = Nothing -- TODO we don't do authentication yet - let (v, v') = rqVersion req - let ver = S.concat [ "HTTP/", bshow v, ".", bshow v' ] - let method = toBS $ Prelude.show (rqMethod req) - let reql = S.intercalate " " [ method, rqURI req, ver ] - let status = rspStatus rsp - let cl = rspContentLength rsp - let referer = maybe Nothing (Just . head) $ Map.lookup "referer" hdrs - let userAgent = maybe "-" head $ Map.lookup "user-agent" hdrs - - msg <- combinedLogEntry host user reql status cl referer userAgent - logMsg logger msg - - ------------------------------------------------------------------------------- -runHTTP :: ByteString -- ^ local host name - -> ByteString -- ^ local ip address - -> Int -- ^ local port - -> ByteString -- ^ remote ip address - -> Int -- ^ remote port - -> Maybe Logger -- ^ access logger - -> Maybe Logger -- ^ error logger - -> Enumerator IO () -- ^ read end of socket - -> Iteratee IO () -- ^ write end of socket - -> (FilePath -> IO ()) -- ^ sendfile end - -> IO () -- ^ timeout tickler - -> ServerHandler -- ^ handler procedure - -> IO () -runHTTP lh lip lp rip rp alog elog - readEnd writeEnd onSendFile tickle handler = - go `catches` [ Handler $ \(e :: AsyncException) -> do - throwIO e - - , Handler $ \(_ :: Backend.TimeoutException) -> return () - - , Handler $ \(e :: SomeException) -> - logE elog $ S.concat [ logPrefix , bshow e ] ] - - where - logPrefix = S.concat [ "[", rip, "]: error: " ] - - go = do - buf <- mkIterateeBuffer - let iter = runServerMonad lh lip lp rip rp (logA alog) (logE elog) $ - httpSession writeEnd buf onSendFile tickle - handler - readEnd iter >>= run - - ------------------------------------------------------------------------------- -sERVER_HEADER :: [ByteString] -sERVER_HEADER = [S.concat ["Snap/", snapServerVersion]] - -snapServerVersion :: ByteString -snapServerVersion = SC.pack $ showVersion $ V.version - ------------------------------------------------------------------------------- -logAccess :: Request -> Response -> ServerMonad () -logAccess req rsp = gets _logAccess >>= (\l -> liftIO $ l req rsp) - ------------------------------------------------------------------------------- -logError :: ByteString -> ServerMonad () -logError s = gets _logError >>= (\l -> liftIO $ l s) - ------------------------------------------------------------------------------- --- | Runs an HTTP session. -httpSession :: Iteratee IO () -- ^ write end of socket - -> ForeignPtr CChar -- ^ iteratee buffer - -> (FilePath -> IO ()) -- ^ sendfile continuation - -> IO () -- ^ timeout tickler - -> ServerHandler -- ^ handler procedure - -> ServerMonad () -httpSession writeEnd' ibuf onSendFile tickle handler = do - - (writeEnd, cancelBuffering) <- - liftIO $ I.unsafeBufferIterateeWithBuffer ibuf writeEnd' - - let killBuffer = writeIORef cancelBuffering True - - liftIO $ debug "Server.httpSession: entered" - mreq <- receiveRequest - -- successfully got a request, so restart timer - liftIO tickle - - case mreq of - (Just req) -> do - logerr <- gets _logError - (req',rspOrig) <- lift $ handler logerr req - let rspTmp = rspOrig { rspHttpVersion = rqVersion req } - checkConnectionClose (rspHttpVersion rspTmp) (rspHeaders rspTmp) - - cc <- gets _forceConnectionClose - let rsp = if cc - then (setHeader "Connection" "close" rspTmp) - else rspTmp - - - liftIO $ debug "Server.httpSession: handled, skipping request body" - srqEnum <- liftIO $ readIORef $ rqBody req' - let (SomeEnumerator rqEnum) = srqEnum - lift $ joinIM $ rqEnum skipToEof - liftIO $ debug "Server.httpSession: request body skipped, sending response" - - date <- liftIO getDateString - let ins = (Map.insert "Date" [date] . Map.insert "Server" sERVER_HEADER) - let rsp' = updateHeaders ins rsp - (bytesSent,_) <- sendResponse rsp' writeEnd ibuf killBuffer onSendFile - - liftIO . debug $ "Server.httpSession: sent " ++ - (Prelude.show bytesSent) ++ " bytes" - - maybe (logAccess req rsp') - (\_ -> logAccess req $ setContentLength bytesSent rsp') - (rspContentLength rsp') - - if cc - then return () - else httpSession writeEnd ibuf onSendFile tickle handler - - Nothing -> return () - ------------------------------------------------------------------------------- -receiveRequest :: ServerMonad (Maybe Request) -receiveRequest = do - mreq <- {-# SCC "receiveRequest/parseRequest" #-} lift parseRequest - - case mreq of - (Just ireq) -> do - req' <- toRequest ireq - setEnumerator req' - req <- parseForm req' - checkConnectionClose (rqVersion req) (rqHeaders req) - return $ Just req - - Nothing -> return Nothing - - - where - -- check: did the client specify "transfer-encoding: chunked"? then we have - -- to honor that. - -- - -- otherwise: check content-length header. if set: only take N bytes from - -- the read end of the socket - -- - -- if no content-length and no chunked encoding, enumerate the entire - -- socket and close afterwards - setEnumerator :: Request -> ServerMonad () - setEnumerator req = - {-# SCC "receiveRequest/setEnumerator" #-} - if isChunked - then liftIO $ writeIORef (rqBody req) - (SomeEnumerator readChunkedTransferEncoding) - else maybe noContentLength hasContentLength mbCL - - where - isChunked = maybe False - ((== ["chunked"]) . map toCI) - (Map.lookup "transfer-encoding" hdrs) - - hasContentLength :: Int -> ServerMonad () - hasContentLength l = do - liftIO $ writeIORef (rqBody req) - (SomeEnumerator e) - where - e :: Enumerator IO a - e = return . joinI . I.take l - - noContentLength :: ServerMonad () - noContentLength = - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . joinI . I.take 0 ) - - - hdrs = rqHeaders req - mbCL = Map.lookup "content-length" hdrs >>= return . Cvt.int . head - - - parseForm :: Request -> ServerMonad Request - parseForm req = - {-# SCC "receiveRequest/parseForm" #-} if doIt then getIt else return req - where - doIt = mbCT == Just "application/x-www-form-urlencoded" - mbCT = liftM head $ Map.lookup "content-type" (rqHeaders req) - - maximumPOSTBodySize :: Int - maximumPOSTBodySize = 10*1024*1024 - - getIt :: ServerMonad Request - getIt = {-# SCC "receiveRequest/parseForm/getIt" #-} do - senum <- liftIO $ readIORef $ rqBody req - let (SomeEnumerator enum) = senum - let i = joinI $ takeNoMoreThan maximumPOSTBodySize stream2stream - iter <- liftIO $ enum i - body <- lift iter - let newParams = parseUrlEncoded $ strictize $ fromWrap body - liftIO $ writeIORef (rqBody req) - (SomeEnumerator $ return . I.joinI . I.take 0) - return $ req { rqParams = rqParams req `mappend` newParams } - - - toRequest (IRequest method uri version kvps) = - {-# SCC "receiveRequest/toRequest" #-} do - localAddr <- gets _localAddress - localPort <- gets _localPort - remoteAddr <- gets _remoteAddr - remotePort <- gets _remotePort - localHostname <- gets _localHostname - - let (serverName, serverPort) = fromMaybe - (localHostname, localPort) - (liftM (parseHost . head) - (Map.lookup "host" hdrs)) - - -- will override in "setEnumerator" - enum <- liftIO $ newIORef $ SomeEnumerator return - - - return $ Request serverName - serverPort - remoteAddr - remotePort - localAddr - localPort - localHostname - isSecure - hdrs - enum - mbContentLength - method - version - cookies - snapletPath - pathInfo - contextPath - uri - queryString - params - - where - snapletPath = "" -- TODO: snaplets in v0.2 - - dropLeadingSlash s = maybe s f mbS - where - f (a,s') = if a == c2w '/' then s' else s - mbS = S.uncons s - - isSecure = False - - hdrs = toHeaders kvps - - mbContentLength = liftM (Cvt.int . head) $ - Map.lookup "content-length" hdrs - - cookies = concat $ - maybe [] - (catMaybes . map parseCookie) - (Map.lookup "cookie" hdrs) - - contextPath = "/" - - parseHost h = (a, Cvt.int (S.drop 1 b)) - where - (a,b) = S.break (== (c2w ':')) h - - params = parseUrlEncoded queryString - - (pathInfo, queryString) = first dropLeadingSlash . second (S.drop 1) $ - S.break (== (c2w '?')) uri - - ------------------------------------------------------------------------------- --- Response must be well-formed here -sendResponse :: Response - -> Iteratee IO a - -> ForeignPtr CChar - -> IO () - -> (FilePath -> IO a) - -> ServerMonad (Int,a) -sendResponse rsp' writeEnd ibuf killBuffering onSendFile = do - rsp <- fixupResponse rsp' - let !headerString = mkHeaderString rsp - - (!x,!bs) <- case (rspBody rsp) of - (Enum e) -> liftIO $ whenEnum headerString e - (SendFile f) -> liftIO $ whenSendFile headerString rsp f - - return $! (bs,x) - - where - whenEnum hs e = do - let enum = enumBS hs >. e - let hl = S.length hs - (x,bs) <- liftIO $ enum (countBytes writeEnd) >>= run - - return (x, bs-hl) - - whenSendFile hs r f = do - -- guaranteed to have a content length here. - enumBS hs writeEnd >>= run - - let !cl = fromJust $ rspContentLength r - x <- onSendFile f - return (x, cl) - - (major,minor) = rspHttpVersion rsp' - - - fmtHdrs hdrs = - {-# SCC "fmtHdrs" #-} - concat xs - where - xs = map f $ Map.toList hdrs - - f (k, ys) = map (g k) ys - - g k y = S.concat [ unCI k, ": ", y, "\r\n" ] - - - noCL :: Response -> ServerMonad Response - noCL r = - {-# SCC "noCL" #-} - do - -- are we in HTTP/1.1? - let sendChunked = (rspHttpVersion r) == (1,1) - if sendChunked - then do - liftIO $ killBuffering - let r' = setHeader "Transfer-Encoding" "chunked" r - let e = writeChunkedTransferEncoding ibuf $ - rspBodyToEnum $ rspBody r - return $ r' { rspBody = Enum e } - - else do - -- HTTP/1.0 and no content-length? We'll have to close the - -- socket. - modify $! \s -> s { _forceConnectionClose = True } - return $ setHeader "Connection" "close" r - - - hasCL :: Int -> Response -> ServerMonad Response - hasCL cl r = - {-# SCC "hasCL" #-} - do - -- set the content-length header - let r' = setHeader "Content-Length" (l2s $ show cl) r - let b = case (rspBody r') of - (Enum e) -> Enum (i e) - (SendFile f) -> SendFile f - - return $ r' { rspBody = b } - - where - i :: Enumerator IO a -> Enumerator IO a - i enum iter = enum (joinI $ takeExactly cl iter) - - - setFileSize :: FilePath -> Response -> ServerMonad Response - setFileSize fp r = - {-# SCC "setFileSize" #-} - do - fs <- liftM fromEnum $ liftIO $ getFileSize fp - return $ r { rspContentLength = Just fs } - - - fixupResponse :: Response -> ServerMonad Response - fixupResponse r = - {-# SCC "fixupResponse" #-} - do - let r' = updateHeaders (Map.delete "Content-Length") r - r'' <- case (rspBody r') of - (Enum _) -> return r' - (SendFile f) -> setFileSize f r' - case (rspContentLength r'') of - Nothing -> noCL r'' - (Just sz) -> hasCL sz r'' - - - bsshow = l2s . show - - - mkHeaderString :: Response -> ByteString - mkHeaderString r = - {-# SCC "mkHeaderString" #-} - S.concat $ concat [hl, hdr, eol] - where - hl = [ "HTTP/" - , bsshow major - , "." - , bsshow minor - , " " - , bsshow $ rspStatus r - , " " - , rspStatusReason r - , "\r\n" ] - - hdr = fmtHdrs $ headers r - - eol = ["\r\n"] - - ------------------------------------------------------------------------------- -checkConnectionClose :: (Int, Int) -> Headers -> ServerMonad () -checkConnectionClose ver hdrs = - -- For HTTP/1.1: - -- if there is an explicit Connection: close, close the socket. - -- For HTTP/1.0: - -- if there is no explicit Connection: Keep-Alive, close the socket. - if (ver == (1,1) && l == Just ["close"]) || - (ver == (1,0) && l /= Just ["Keep-Alive"]) - then modify $ \s -> s { _forceConnectionClose = True } - else return () - where - l = liftM (map tl) $ Map.lookup "Connection" hdrs - tl = S.map (c2w . toLower . w2c) - - ------------------------------------------------------------------------------- --- FIXME: whitespace-trim the values here. -toHeaders :: [(ByteString,ByteString)] -> Headers -toHeaders kvps = foldl' f Map.empty kvps' - where - kvps' = map (first toCI . second (:[])) kvps - f m (k,v) = Map.insertWith' (flip (++)) k v m - - ------------------------------------------------------------------------------- -getFileSize :: FilePath -> IO FileOffset -getFileSize fp = liftM fileSize $ getFileStatus fp - - -l2s :: L.ByteString -> S.ByteString -l2s = S.concat . L.toChunks - - -toBS :: String -> ByteString -toBS = S.pack . map c2w -- diff --git a/static/docs/0.2.4/snap-server/src/System-FastLogger.html b/static/docs/0.2.4/snap-server/src/System-FastLogger.html deleted file mode 100644 index d2f4dce..0000000 --- a/static/docs/0.2.4/snap-server/src/System-FastLogger.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - -
{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module System.FastLogger -( Logger -, timestampedLogEntry -, combinedLogEntry -, newLogger -, logMsg -, stopLogger -) where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as S -import qualified Data.ByteString.Lazy.Char8 as L -import Data.ByteString.Internal (c2w) -import Data.DList (DList) -import qualified Data.DList as D -import Data.IORef -import Data.Maybe -import Data.Serialize.Put -import Prelude hiding (catch, show) -import qualified Prelude -import System.IO -import Text.Show.ByteString hiding (runPut) - -import Snap.Internal.Http.Server.Date - - --- | Holds the state for a logger. -data Logger = Logger - { _queuedMessages :: !(IORef (DList ByteString)) - , _dataWaiting :: !(MVar ()) - , _loggerPath :: !(FilePath) - , _loggingThread :: !(MVar ThreadId) } - - --- | Creates a new logger, logging to the given file. If the file argument is --- \"-\", then log to stdout; if it's \"stderr\" then we log to stderr, --- otherwise we log to a regular file in append mode. The file is closed and --- re-opened every 15 minutes to facilitate external log rotation. -newLogger :: FilePath -> IO Logger -newLogger fp = do - q <- newIORef D.empty - dw <- newEmptyMVar - th <- newEmptyMVar - - let lg = Logger q dw fp th - - tid <- forkIO $ loggingThread lg - putMVar th tid - - return lg - --- | Prepares a log message with the time prepended. -timestampedLogEntry :: ByteString -> IO ByteString -timestampedLogEntry msg = do - timeStr <- getLogDateString - - return $! runPut $! do - putWord8 $ c2w '[' - putByteString timeStr - putByteString "] " - putByteString msg - - --- | Prepares a log message in \"combined\" format. -combinedLogEntry :: ByteString -- ^ remote host - -> Maybe ByteString -- ^ remote user - -> ByteString -- ^ request line (up to you to ensure - -- there are no quotes in here) - -> Int -- ^ status code - -> Maybe Int -- ^ num bytes sent - -> Maybe ByteString -- ^ referer (up to you to ensure - -- there are no quotes in here) - -> ByteString -- ^ user agent (up to you to ensure - -- there are no quotes in here) - -> IO ByteString -combinedLogEntry !host !mbUser !req !status !mbNumBytes !mbReferer !userAgent = do - let user = fromMaybe "-" mbUser - let numBytes = maybe "-" (\s -> strict $ show s) mbNumBytes - let referer = maybe "-" (\s -> S.concat ["\"", s, "\""]) mbReferer - - timeStr <- getLogDateString - - let !p = [ host - , " - " - , user - , " [" - , timeStr - , "] \"" - , req - , "\" " - , strict $ show status - , " " - , numBytes - , " " - , referer - , " \"" - , userAgent - , "\"" ] - - let !output = S.concat p - - return $! output - - - where - strict = S.concat . L.toChunks - - --- | Sends out a log message verbatim with a newline appended. Note: --- if you want a fancy log message you'll have to format it yourself --- (or use 'combinedLogEntry'). -logMsg :: Logger -> ByteString -> IO () -logMsg !lg !s = do - let !s' = S.snoc s '\n' - atomicModifyIORef (_queuedMessages lg) $ \d -> (D.snoc d s',()) - tryPutMVar (_dataWaiting lg) () >> return () - - -loggingThread :: Logger -> IO () -loggingThread (Logger queue notifier filePath _) = do - initialize >>= go - - where - openIt = if filePath == "-" - then return stdout - else if filePath == "stderr" - then return stderr - else openFile filePath AppendMode - - closeIt h = if filePath == "-" || filePath == "stderr" - then return () - else hClose h - - go (href, lastOpened) = - (loop (href, lastOpened)) - `catches` - [ Handler $ \(_::AsyncException) -> killit (href, lastOpened) - , Handler $ \(e::SomeException) -> do - hPutStrLn stderr $ "logger got exception: " ++ Prelude.show e - threadDelay 20000000 - go (href, lastOpened) ] - - - initialize = do - lh <- openIt - href <- newIORef lh - t <- getCurrentDateTime - tref <- newIORef t - return (href, tref) - - - killit (href, lastOpened) = do - flushIt (href, lastOpened) - h <- readIORef href - closeIt h - - - flushIt (!href, !lastOpened) = do - dl <- atomicModifyIORef queue $ \x -> (D.empty,x) - - let !msgs = D.toList dl - let !s = L.fromChunks msgs - h <- readIORef href - L.hPut h s - hFlush h - - -- close the file every 15 minutes (for log rotation) - t <- getCurrentDateTime - old <- readIORef lastOpened - - if t-old > 900 - then do - closeIt h - openIt >>= writeIORef href - writeIORef lastOpened t - else return () - - - loop !d = do - -- wait on the notification mvar - _ <- takeMVar notifier - - -- grab the queued messages and write them out - flushIt d - - -- at least five seconds between log dumps - threadDelay 5000000 - loop d - - --- | Kills a logger thread, causing any unwritten contents to be --- flushed out to disk -stopLogger :: Logger -> IO () -stopLogger lg = withMVar (_loggingThread lg) killThread -- diff --git a/static/docs/0.2.4/snap-server/src/hscolour.css b/static/docs/0.2.4/snap-server/src/hscolour.css deleted file mode 100644 index 150e4d0..0000000 --- a/static/docs/0.2.4/snap-server/src/hscolour.css +++ /dev/null @@ -1,15 +0,0 @@ -body { font-size: 90%; } - -pre, code, body { - font-family: Monaco, - "DejaVu Sans Mono", - "Bitstream Vera Sans Mono", - "Lucida Console", - monospace; -} - -.hs-keyglyph, .hs-layout {color: #5200A3;} -.hs-keyword {color: #3465a4; font-weight: bold;} -.hs-comment, .hs-comment a {color: #579; } -.hs-str, .hs-chr {color: #141B24;} -.hs-keyword, .hs-conid, .hs-varid, .hs-conop, .hs-varop, .hs-num, .hs-cpp, .hs-sel, .hs-definition {} diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Constants.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Constants.hs.html index 1a6757e..7bf17d9 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Constants.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Constants.hs.html @@ -15,255 +15,268 @@ 4 import Data.ByteString.Char8 (ByteString) 5 import qualified Data.Map as Map 6 import Data.Map (Map) - 7 - 8 htmlEntityLookupTable :: Map ByteString ByteString - 9 htmlEntityLookupTable = Map.fromList [ - 10 ("acute" , "\xc2\xb4") - 11 , ("cedil" , "\xc2\xb8") - 12 , ("circ" , "\xcb\x86") - 13 , ("macr" , "\xc2\xaf") - 14 , ("middot" , "\xc2\xb7") - 15 , ("tilde" , "\xcb\x9c") - 16 , ("uml" , "\xc2\xa8") - 17 , ("Aacute" , "\xc3\x81") - 18 , ("aacute" , "\xc3\xa1") - 19 , ("Acirc" , "\xc3\x82") - 20 , ("acirc" , "\xc3\xa2") - 21 , ("AElig" , "\xc3\x86") - 22 , ("aelig" , "\xc3\xa6") - 23 , ("Agrave" , "\xc3\x80") - 24 , ("agrave" , "\xc3\xa0") - 25 , ("Aring" , "\xc3\x85") - 26 , ("aring" , "\xc3\xa5") - 27 , ("Atilde" , "\xc3\x83") - 28 , ("atilde" , "\xc3\xa3") - 29 , ("Auml" , "\xc3\x84") - 30 , ("auml" , "\xc3\xa4") - 31 , ("Ccedil" , "\xc3\x87") - 32 , ("ccedil" , "\xc3\xa7") - 33 , ("Eacute" , "\xc3\x89") - 34 , ("eacute" , "\xc3\xa9") - 35 , ("Ecirc" , "\xc3\x8a") - 36 , ("ecirc" , "\xc3\xaa") - 37 , ("Egrave" , "\xc3\x88") - 38 , ("egrave" , "\xc3\xa8") - 39 , ("ETH" , "\xc3\x90") - 40 , ("eth" , "\xc3\xb0") - 41 , ("Euml" , "\xc3\x8b") - 42 , ("euml" , "\xc3\xab") - 43 , ("Iacute" , "\xc3\x8d") - 44 , ("iacute" , "\xc3\xad") - 45 , ("Icirc" , "\xc3\x8e") - 46 , ("icirc" , "\xc3\xae") - 47 , ("Igrave" , "\xc3\x8c") - 48 , ("igrave" , "\xc3\xac") - 49 , ("Iuml" , "\xc3\x8f") - 50 , ("iuml" , "\xc3\xaf") - 51 , ("Ntilde" , "\xc3\x91") - 52 , ("ntilde" , "\xc3\xb1") - 53 , ("Oacute" , "\xc3\x93") - 54 , ("oacute" , "\xc3\xb3") - 55 , ("Ocirc" , "\xc3\x94") - 56 , ("ocirc" , "\xc3\xb4") - 57 , ("OElig" , "\xc5\x92") - 58 , ("oelig" , "\xc5\x93") - 59 , ("Ograve" , "\xc3\x92") - 60 , ("ograve" , "\xc3\xb2") - 61 , ("Oslash" , "\xc3\x98") - 62 , ("oslash" , "\xc3\xb8") - 63 , ("Otilde" , "\xc3\x95") - 64 , ("otilde" , "\xc3\xb5") - 65 , ("Ouml" , "\xc3\x96") - 66 , ("ouml" , "\xc3\xb6") - 67 , ("Scaron" , "\xc5\xa0") - 68 , ("scaron" , "\xc5\xa1") - 69 , ("szlig" , "\xc3\x9f") - 70 , ("THORN" , "\xc3\x9e") - 71 , ("thorn" , "\xc3\xbe") - 72 , ("Uacute" , "\xc3\x9a") - 73 , ("uacute" , "\xc3\xba") - 74 , ("Ucirc" , "\xc3\x9b") - 75 , ("ucirc" , "\xc3\xbb") - 76 , ("Ugrave" , "\xc3\x99") - 77 , ("ugrave" , "\xc3\xb9") - 78 , ("Uuml" , "\xc3\x9c") - 79 , ("uuml" , "\xc3\xbc") - 80 , ("Yacute" , "\xc3\x9d") - 81 , ("yacute" , "\xc3\xbd") - 82 , ("yuml" , "\xc3\xbf") - 83 , ("Yuml" , "\xc5\xb8") - 84 , ("cent" , "\xc2\xa2") - 85 , ("curren" , "\xc2\xa4") - 86 , ("euro" , "\xe2\x82\xac") - 87 , ("pound" , "\xc2\xa3") - 88 , ("yen" , "\xc2\xa5") - 89 , ("brvbar" , "\xc2\xa6") - 90 , ("bull" , "\xe2\x80\xa2") - 91 , ("copy" , "\xc2\xa9") - 92 , ("dagger" , "\xe2\x80\xa0") - 93 , ("Dagger" , "\xe2\x80\xa1") - 94 , ("frasl" , "\xe2\x81\x84") - 95 , ("hellip" , "\xe2\x80\xa6") - 96 , ("iexcl" , "\xc2\xa1") - 97 , ("image" , "\xe2\x84\x91") - 98 , ("iquest" , "\xc2\xbf") - 99 , ("mdash" , "\xe2\x80\x94") - 100 , ("ndash" , "\xe2\x80\x93") - 101 , ("not" , "\xc2\xac") - 102 , ("oline" , "\xe2\x80\xbe") - 103 , ("ordf" , "\xc2\xaa") - 104 , ("ordm" , "\xc2\xba") - 105 , ("para" , "\xc2\xb6") - 106 , ("permil" , "\xe2\x80\xb0") - 107 , ("prime" , "\xe2\x80\xb2") - 108 , ("Prime" , "\xe2\x80\xb3") - 109 , ("real" , "\xe2\x84\x9c") - 110 , ("reg" , "\xc2\xae") - 111 , ("sect" , "\xc2\xa7") - 112 , ("shy" , "\173") - 113 , ("sup1" , "\xc2\xb9") - 114 , ("trade" , "\xe2\x84\xa2") - 115 , ("weierp" , "\xe2\x84\x98") - 116 , ("bdquo" , "\xe2\x80\x9e") - 117 , ("laquo" , "\xc2\xab") - 118 , ("ldquo" , "\xe2\x80\x9c") - 119 , ("lsaquo" , "\xe2\x80\xb9") - 120 , ("lsquo" , "\xe2\x80\x98") - 121 , ("raquo" , "\xc2\xbb") - 122 , ("rdquo" , "\xe2\x80\x9d") - 123 , ("rsaquo" , "\xe2\x80\xba") - 124 , ("rsquo" , "\xe2\x80\x99") - 125 , ("sbquo" , "\xe2\x80\x9a") - 126 , ("emsp" , "\xe2\x80\x83") - 127 , ("ensp" , "\xe2\x80\x82") - 128 , ("nbsp" , "\x20") - 129 , ("thinsp" , "\xe2\x80\x89") - 130 , ("zwj" , "\xe2\x80\x8d") - 131 , ("zwnj" , "\xe2\x80\x8c") - 132 , ("deg" , "\xc2\xb0") - 133 , ("divide" , "\xc3\xb7") - 134 , ("frac12" , "\xc2\xbd") - 135 , ("frac14" , "\xc2\xbc") - 136 , ("frac34" , "\xc2\xbe") - 137 , ("ge" , "\xe2\x89\xa5") - 138 , ("le" , "\xe2\x89\xa4") - 139 , ("minus" , "\xe2\x88\x92") - 140 , ("sup2" , "\xc2\xb2") - 141 , ("sup3" , "\xc2\xb3") - 142 , ("times" , "\xc3\x97") - 143 , ("alefsym" , "\xe2\x84\xb5") - 144 , ("and" , "\xe2\x88\xa7") - 145 , ("ang" , "\xe2\x88\xa0") - 146 , ("asymp" , "\xe2\x89\x88") - 147 , ("cap" , "\xe2\x88\xa9") - 148 , ("cong" , "\xe2\x89\x85") - 149 , ("cup" , "\xe2\x88\xaa") - 150 , ("empty" , "\xe2\x88\x85") - 151 , ("equiv" , "\xe2\x89\xa1") - 152 , ("exist" , "\xe2\x88\x83") - 153 , ("fnof" , "\xc6\x92") - 154 , ("forall" , "\xe2\x88\x80") - 155 , ("infin" , "\xe2\x88\x9e") - 156 , ("int" , "\xe2\x88\xab") - 157 , ("isin" , "\xe2\x88\x88") - 158 , ("lang" , "\xe3\x80\x88") - 159 , ("lceil" , "\xe2\x8c\x88") - 160 , ("lfloor" , "\xe2\x8c\x8a") - 161 , ("lowast" , "\xe2\x88\x97") - 162 , ("micro" , "\xc2\xb5") - 163 , ("nabla" , "\xe2\x88\x87") - 164 , ("ne" , "\xe2\x89\xa0") - 165 , ("ni" , "\xe2\x88\x8b") - 166 , ("notin" , "\xe2\x88\x89") - 167 , ("nsub" , "\xe2\x8a\x84") - 168 , ("oplus" , "\xe2\x8a\x95") - 169 , ("or" , "\xe2\x88\xa8") - 170 , ("otimes" , "\xe2\x8a\x97") - 171 , ("part" , "\xe2\x88\x82") - 172 , ("perp" , "\xe2\x8a\xa5") - 173 , ("plusmn" , "\xc2\xb1") - 174 , ("prod" , "\xe2\x88\x8f") - 175 , ("prop" , "\xe2\x88\x9d") - 176 , ("radic" , "\xe2\x88\x9a") - 177 , ("rang" , "\xe3\x80\x89") - 178 , ("rceil" , "\xe2\x8c\x89") - 179 , ("rfloor" , "\xe2\x8c\x8b") - 180 , ("sdot" , "\xe2\x8b\x85") - 181 , ("sim" , "\xe2\x88\xbc") - 182 , ("sub" , "\xe2\x8a\x82") - 183 , ("sube" , "\xe2\x8a\x86") - 184 , ("sum" , "\xe2\x88\x91") - 185 , ("sup" , "\xe2\x8a\x83") - 186 , ("supe" , "\xe2\x8a\x87") - 187 , ("there4" , "\xe2\x88\xb4") - 188 , ("Alpha" , "\xce\x91") - 189 , ("alpha" , "\xce\xb1") - 190 , ("Beta" , "\xce\x92") - 191 , ("beta" , "\xce\xb2") - 192 , ("Chi" , "\xce\xa7") - 193 , ("chi" , "\xcf\x87") - 194 , ("Delta" , "\xce\x94") - 195 , ("delta" , "\xce\xb4") - 196 , ("Epsilon" , "\xce\x95") - 197 , ("epsilon" , "\xce\xb5") - 198 , ("Eta" , "\xce\x97") - 199 , ("eta" , "\xce\xb7") - 200 , ("Gamma" , "\xce\x93") - 201 , ("gamma" , "\xce\xb3") - 202 , ("Iota" , "\xce\x99") - 203 , ("iota" , "\xce\xb9") - 204 , ("Kappa" , "\xce\x9a") - 205 , ("kappa" , "\xce\xba") - 206 , ("Lambda" , "\xce\x9b") - 207 , ("lambda" , "\xce\xbb") - 208 , ("Mu" , "\xce\x9c") - 209 , ("mu" , "\xce\xbc") - 210 , ("Nu" , "\xce\x9d") - 211 , ("nu" , "\xce\xbd") - 212 , ("Omega" , "\xce\xa9") - 213 , ("omega" , "\xcf\x89") - 214 , ("Omicron" , "\xce\x9f") - 215 , ("omicron" , "\xce\xbf") - 216 , ("Phi" , "\xce\xa6") - 217 , ("phi" , "\xcf\x86") - 218 , ("Pi" , "\xce\xa0") - 219 , ("pi" , "\xcf\x80") - 220 , ("piv" , "\xcf\x96") - 221 , ("Psi" , "\xce\xa8") - 222 , ("psi" , "\xcf\x88") - 223 , ("Rho" , "\xce\xa1") - 224 , ("rho" , "\xcf\x81") - 225 , ("Sigma" , "\xce\xa3") - 226 , ("sigma" , "\xcf\x83") - 227 , ("sigmaf" , "\xcf\x82") - 228 , ("Tau" , "\xce\xa4") - 229 , ("tau" , "\xcf\x84") - 230 , ("Theta" , "\xce\x98") - 231 , ("theta" , "\xce\xb8") - 232 , ("thetasym" , "\xcf\x91") - 233 , ("upsih" , "\xcf\x92") - 234 , ("Upsilon" , "\xce\xa5") - 235 , ("upsilon" , "\xcf\x85") - 236 , ("Xi" , "\xce\x9e") - 237 , ("xi" , "\xce\xbe") - 238 , ("Zeta" , "\xce\x96") - 239 , ("zeta" , "\xce\xb6") - 240 , ("crarr" , "\xe2\x86\xb5") - 241 , ("darr" , "\xe2\x86\x93") - 242 , ("dArr" , "\xe2\x87\x93") - 243 , ("harr" , "\xe2\x86\x94") - 244 , ("hArr" , "\xe2\x87\x94") - 245 , ("larr" , "\xe2\x86\x90") - 246 , ("lArr" , "\xe2\x87\x90") - 247 , ("rarr" , "\xe2\x86\x92") - 248 , ("rArr" , "\xe2\x87\x92") - 249 , ("uarr" , "\xe2\x86\x91") - 250 , ("uArr" , "\xe2\x87\x91") - 251 , ("clubs" , "\xe2\x99\xa3") - 252 , ("diams" , "\xe2\x99\xa6") - 253 , ("hearts" , "\xe2\x99\xa5") - 254 , ("spades" , "\xe2\x99\xa0") - 255 , ("loz" , "\xe2\x97\x8a") ] + 7 import Text.XML.Expat.Tree + 8 + 9 + 10 ------------------------------------------------------------------------------ + 11 -- | Options passed to hexpat for XML parsing. + 12 heistExpatOptions :: ParserOptions ByteString ByteString + 13 heistExpatOptions = + 14 defaultParserOptions { + 15 parserEncoding = Just UTF8 + 16 , entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) + 17 } + 18 + 19 ------------------------------------------------------------------------------ + 20 -- | Character entity references for HTML. + 21 htmlEntityLookupTable :: Map ByteString ByteString + 22 htmlEntityLookupTable = Map.fromList [ + 23 ("acute" , "\xc2\xb4") + 24 , ("cedil" , "\xc2\xb8") + 25 , ("circ" , "\xcb\x86") + 26 , ("macr" , "\xc2\xaf") + 27 , ("middot" , "\xc2\xb7") + 28 , ("tilde" , "\xcb\x9c") + 29 , ("uml" , "\xc2\xa8") + 30 , ("Aacute" , "\xc3\x81") + 31 , ("aacute" , "\xc3\xa1") + 32 , ("Acirc" , "\xc3\x82") + 33 , ("acirc" , "\xc3\xa2") + 34 , ("AElig" , "\xc3\x86") + 35 , ("aelig" , "\xc3\xa6") + 36 , ("Agrave" , "\xc3\x80") + 37 , ("agrave" , "\xc3\xa0") + 38 , ("Aring" , "\xc3\x85") + 39 , ("aring" , "\xc3\xa5") + 40 , ("Atilde" , "\xc3\x83") + 41 , ("atilde" , "\xc3\xa3") + 42 , ("Auml" , "\xc3\x84") + 43 , ("auml" , "\xc3\xa4") + 44 , ("Ccedil" , "\xc3\x87") + 45 , ("ccedil" , "\xc3\xa7") + 46 , ("Eacute" , "\xc3\x89") + 47 , ("eacute" , "\xc3\xa9") + 48 , ("Ecirc" , "\xc3\x8a") + 49 , ("ecirc" , "\xc3\xaa") + 50 , ("Egrave" , "\xc3\x88") + 51 , ("egrave" , "\xc3\xa8") + 52 , ("ETH" , "\xc3\x90") + 53 , ("eth" , "\xc3\xb0") + 54 , ("Euml" , "\xc3\x8b") + 55 , ("euml" , "\xc3\xab") + 56 , ("Iacute" , "\xc3\x8d") + 57 , ("iacute" , "\xc3\xad") + 58 , ("Icirc" , "\xc3\x8e") + 59 , ("icirc" , "\xc3\xae") + 60 , ("Igrave" , "\xc3\x8c") + 61 , ("igrave" , "\xc3\xac") + 62 , ("Iuml" , "\xc3\x8f") + 63 , ("iuml" , "\xc3\xaf") + 64 , ("Ntilde" , "\xc3\x91") + 65 , ("ntilde" , "\xc3\xb1") + 66 , ("Oacute" , "\xc3\x93") + 67 , ("oacute" , "\xc3\xb3") + 68 , ("Ocirc" , "\xc3\x94") + 69 , ("ocirc" , "\xc3\xb4") + 70 , ("OElig" , "\xc5\x92") + 71 , ("oelig" , "\xc5\x93") + 72 , ("Ograve" , "\xc3\x92") + 73 , ("ograve" , "\xc3\xb2") + 74 , ("Oslash" , "\xc3\x98") + 75 , ("oslash" , "\xc3\xb8") + 76 , ("Otilde" , "\xc3\x95") + 77 , ("otilde" , "\xc3\xb5") + 78 , ("Ouml" , "\xc3\x96") + 79 , ("ouml" , "\xc3\xb6") + 80 , ("Scaron" , "\xc5\xa0") + 81 , ("scaron" , "\xc5\xa1") + 82 , ("szlig" , "\xc3\x9f") + 83 , ("THORN" , "\xc3\x9e") + 84 , ("thorn" , "\xc3\xbe") + 85 , ("Uacute" , "\xc3\x9a") + 86 , ("uacute" , "\xc3\xba") + 87 , ("Ucirc" , "\xc3\x9b") + 88 , ("ucirc" , "\xc3\xbb") + 89 , ("Ugrave" , "\xc3\x99") + 90 , ("ugrave" , "\xc3\xb9") + 91 , ("Uuml" , "\xc3\x9c") + 92 , ("uuml" , "\xc3\xbc") + 93 , ("Yacute" , "\xc3\x9d") + 94 , ("yacute" , "\xc3\xbd") + 95 , ("yuml" , "\xc3\xbf") + 96 , ("Yuml" , "\xc5\xb8") + 97 , ("cent" , "\xc2\xa2") + 98 , ("curren" , "\xc2\xa4") + 99 , ("euro" , "\xe2\x82\xac") + 100 , ("pound" , "\xc2\xa3") + 101 , ("yen" , "\xc2\xa5") + 102 , ("brvbar" , "\xc2\xa6") + 103 , ("bull" , "\xe2\x80\xa2") + 104 , ("copy" , "\xc2\xa9") + 105 , ("dagger" , "\xe2\x80\xa0") + 106 , ("Dagger" , "\xe2\x80\xa1") + 107 , ("frasl" , "\xe2\x81\x84") + 108 , ("hellip" , "\xe2\x80\xa6") + 109 , ("iexcl" , "\xc2\xa1") + 110 , ("image" , "\xe2\x84\x91") + 111 , ("iquest" , "\xc2\xbf") + 112 , ("mdash" , "\xe2\x80\x94") + 113 , ("ndash" , "\xe2\x80\x93") + 114 , ("not" , "\xc2\xac") + 115 , ("oline" , "\xe2\x80\xbe") + 116 , ("ordf" , "\xc2\xaa") + 117 , ("ordm" , "\xc2\xba") + 118 , ("para" , "\xc2\xb6") + 119 , ("permil" , "\xe2\x80\xb0") + 120 , ("prime" , "\xe2\x80\xb2") + 121 , ("Prime" , "\xe2\x80\xb3") + 122 , ("real" , "\xe2\x84\x9c") + 123 , ("reg" , "\xc2\xae") + 124 , ("sect" , "\xc2\xa7") + 125 , ("shy" , "\173") + 126 , ("sup1" , "\xc2\xb9") + 127 , ("trade" , "\xe2\x84\xa2") + 128 , ("weierp" , "\xe2\x84\x98") + 129 , ("bdquo" , "\xe2\x80\x9e") + 130 , ("laquo" , "\xc2\xab") + 131 , ("ldquo" , "\xe2\x80\x9c") + 132 , ("lsaquo" , "\xe2\x80\xb9") + 133 , ("lsquo" , "\xe2\x80\x98") + 134 , ("raquo" , "\xc2\xbb") + 135 , ("rdquo" , "\xe2\x80\x9d") + 136 , ("rsaquo" , "\xe2\x80\xba") + 137 , ("rsquo" , "\xe2\x80\x99") + 138 , ("sbquo" , "\xe2\x80\x9a") + 139 , ("emsp" , "\xe2\x80\x83") + 140 , ("ensp" , "\xe2\x80\x82") + 141 , ("nbsp" , "\x20") + 142 , ("thinsp" , "\xe2\x80\x89") + 143 , ("zwj" , "\xe2\x80\x8d") + 144 , ("zwnj" , "\xe2\x80\x8c") + 145 , ("deg" , "\xc2\xb0") + 146 , ("divide" , "\xc3\xb7") + 147 , ("frac12" , "\xc2\xbd") + 148 , ("frac14" , "\xc2\xbc") + 149 , ("frac34" , "\xc2\xbe") + 150 , ("ge" , "\xe2\x89\xa5") + 151 , ("le" , "\xe2\x89\xa4") + 152 , ("minus" , "\xe2\x88\x92") + 153 , ("sup2" , "\xc2\xb2") + 154 , ("sup3" , "\xc2\xb3") + 155 , ("times" , "\xc3\x97") + 156 , ("alefsym" , "\xe2\x84\xb5") + 157 , ("and" , "\xe2\x88\xa7") + 158 , ("ang" , "\xe2\x88\xa0") + 159 , ("asymp" , "\xe2\x89\x88") + 160 , ("cap" , "\xe2\x88\xa9") + 161 , ("cong" , "\xe2\x89\x85") + 162 , ("cup" , "\xe2\x88\xaa") + 163 , ("empty" , "\xe2\x88\x85") + 164 , ("equiv" , "\xe2\x89\xa1") + 165 , ("exist" , "\xe2\x88\x83") + 166 , ("fnof" , "\xc6\x92") + 167 , ("forall" , "\xe2\x88\x80") + 168 , ("infin" , "\xe2\x88\x9e") + 169 , ("int" , "\xe2\x88\xab") + 170 , ("isin" , "\xe2\x88\x88") + 171 , ("lang" , "\xe3\x80\x88") + 172 , ("lceil" , "\xe2\x8c\x88") + 173 , ("lfloor" , "\xe2\x8c\x8a") + 174 , ("lowast" , "\xe2\x88\x97") + 175 , ("micro" , "\xc2\xb5") + 176 , ("nabla" , "\xe2\x88\x87") + 177 , ("ne" , "\xe2\x89\xa0") + 178 , ("ni" , "\xe2\x88\x8b") + 179 , ("notin" , "\xe2\x88\x89") + 180 , ("nsub" , "\xe2\x8a\x84") + 181 , ("oplus" , "\xe2\x8a\x95") + 182 , ("or" , "\xe2\x88\xa8") + 183 , ("otimes" , "\xe2\x8a\x97") + 184 , ("part" , "\xe2\x88\x82") + 185 , ("perp" , "\xe2\x8a\xa5") + 186 , ("plusmn" , "\xc2\xb1") + 187 , ("prod" , "\xe2\x88\x8f") + 188 , ("prop" , "\xe2\x88\x9d") + 189 , ("radic" , "\xe2\x88\x9a") + 190 , ("rang" , "\xe3\x80\x89") + 191 , ("rceil" , "\xe2\x8c\x89") + 192 , ("rfloor" , "\xe2\x8c\x8b") + 193 , ("sdot" , "\xe2\x8b\x85") + 194 , ("sim" , "\xe2\x88\xbc") + 195 , ("sub" , "\xe2\x8a\x82") + 196 , ("sube" , "\xe2\x8a\x86") + 197 , ("sum" , "\xe2\x88\x91") + 198 , ("sup" , "\xe2\x8a\x83") + 199 , ("supe" , "\xe2\x8a\x87") + 200 , ("there4" , "\xe2\x88\xb4") + 201 , ("Alpha" , "\xce\x91") + 202 , ("alpha" , "\xce\xb1") + 203 , ("Beta" , "\xce\x92") + 204 , ("beta" , "\xce\xb2") + 205 , ("Chi" , "\xce\xa7") + 206 , ("chi" , "\xcf\x87") + 207 , ("Delta" , "\xce\x94") + 208 , ("delta" , "\xce\xb4") + 209 , ("Epsilon" , "\xce\x95") + 210 , ("epsilon" , "\xce\xb5") + 211 , ("Eta" , "\xce\x97") + 212 , ("eta" , "\xce\xb7") + 213 , ("Gamma" , "\xce\x93") + 214 , ("gamma" , "\xce\xb3") + 215 , ("Iota" , "\xce\x99") + 216 , ("iota" , "\xce\xb9") + 217 , ("Kappa" , "\xce\x9a") + 218 , ("kappa" , "\xce\xba") + 219 , ("Lambda" , "\xce\x9b") + 220 , ("lambda" , "\xce\xbb") + 221 , ("Mu" , "\xce\x9c") + 222 , ("mu" , "\xce\xbc") + 223 , ("Nu" , "\xce\x9d") + 224 , ("nu" , "\xce\xbd") + 225 , ("Omega" , "\xce\xa9") + 226 , ("omega" , "\xcf\x89") + 227 , ("Omicron" , "\xce\x9f") + 228 , ("omicron" , "\xce\xbf") + 229 , ("Phi" , "\xce\xa6") + 230 , ("phi" , "\xcf\x86") + 231 , ("Pi" , "\xce\xa0") + 232 , ("pi" , "\xcf\x80") + 233 , ("piv" , "\xcf\x96") + 234 , ("Psi" , "\xce\xa8") + 235 , ("psi" , "\xcf\x88") + 236 , ("Rho" , "\xce\xa1") + 237 , ("rho" , "\xcf\x81") + 238 , ("Sigma" , "\xce\xa3") + 239 , ("sigma" , "\xcf\x83") + 240 , ("sigmaf" , "\xcf\x82") + 241 , ("Tau" , "\xce\xa4") + 242 , ("tau" , "\xcf\x84") + 243 , ("Theta" , "\xce\x98") + 244 , ("theta" , "\xce\xb8") + 245 , ("thetasym" , "\xcf\x91") + 246 , ("upsih" , "\xcf\x92") + 247 , ("Upsilon" , "\xce\xa5") + 248 , ("upsilon" , "\xcf\x85") + 249 , ("Xi" , "\xce\x9e") + 250 , ("xi" , "\xce\xbe") + 251 , ("Zeta" , "\xce\x96") + 252 , ("zeta" , "\xce\xb6") + 253 , ("crarr" , "\xe2\x86\xb5") + 254 , ("darr" , "\xe2\x86\x93") + 255 , ("dArr" , "\xe2\x87\x93") + 256 , ("harr" , "\xe2\x86\x94") + 257 , ("hArr" , "\xe2\x87\x94") + 258 , ("larr" , "\xe2\x86\x90") + 259 , ("lArr" , "\xe2\x87\x90") + 260 , ("rarr" , "\xe2\x86\x92") + 261 , ("rArr" , "\xe2\x87\x92") + 262 , ("uarr" , "\xe2\x86\x91") + 263 , ("uArr" , "\xe2\x87\x91") + 264 , ("clubs" , "\xe2\x99\xa3") + 265 , ("diams" , "\xe2\x99\xa6") + 266 , ("hearts" , "\xe2\x99\xa5") + 267 , ("spades" , "\xe2\x99\xa0") + 268 , ("loz" , "\xe2\x97\x8a") ] diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Internal.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Internal.hs.html index 2fbd5c4..a242c9d 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Internal.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Internal.hs.html @@ -16,513 +16,544 @@ 5 module Text.Templating.Heist.Internal where 6 7 ------------------------------------------------------------------------------ - 8 import Control.Exception (SomeException) - 9 import Control.Monad.CatchIO - 10 import Control.Monad.RWS.Strict - 11 import Data.ByteString.Char8 (ByteString) - 12 import qualified Data.ByteString.Char8 as B - 13 import qualified Data.ByteString.Lazy as L - 14 import Data.Either - 15 import qualified Data.Foldable as F - 16 import Data.List - 17 import qualified Data.Map as Map - 18 import Data.Map (Map) - 19 import Data.Typeable - 20 import Prelude hiding (catch) - 21 import System.Directory.Tree hiding (name) - 22 import Text.XML.Expat.Format - 23 import qualified Text.XML.Expat.Tree as X - 24 - 25 ------------------------------------------------------------------------------ - 26 import Text.Templating.Heist.Constants - 27 - 28 ------------------------------------------------------------------------------ - 29 -- Types - 30 ------------------------------------------------------------------------------ + 8 import Control.Applicative + 9 import Control.Exception (SomeException) + 10 import Control.Monad.CatchIO + 11 import Control.Monad.RWS.Strict + 12 import qualified Data.Attoparsec.Char8 as AP + 13 import Data.ByteString.Char8 (ByteString) + 14 import qualified Data.ByteString.Char8 as B + 15 import qualified Data.ByteString.Lazy as L + 16 import Data.Either + 17 import qualified Data.Foldable as F + 18 import Data.List + 19 import qualified Data.Map as Map + 20 import Data.Maybe + 21 import Prelude hiding (catch) + 22 import System.Directory.Tree hiding (name) + 23 import System.FilePath + 24 import Text.XML.Expat.Format + 25 import qualified Text.XML.Expat.Tree as X + 26 + 27 ------------------------------------------------------------------------------ + 28 import Text.Templating.Heist.Constants + 29 import Text.Templating.Heist.Types + 30 31 - 32 -- | Heist templates are XML documents. The hexpat library is polymorphic over - 33 -- the type of strings, so here we define a 'Node' alias to fix the string - 34 -- types of the tag names and tag bodies to 'ByteString'. - 35 type Node = X.Node ByteString ByteString - 36 - 37 - 38 ------------------------------------------------------------------------------ - 39 -- | A 'Template' is a forest of XML nodes. - 40 type Template = [Node] - 41 - 42 - 43 ------------------------------------------------------------------------------ - 44 -- | Reversed list of directories - 45 type TPath = [ByteString] + 32 ------------------------------------------------------------------------------ + 33 -- | Restores the components of TemplateState that can get modified in + 34 -- template calls. You should use this function instead of @putTS@ to restore + 35 -- an old state. Thas was needed because doctypes needs to be in a "global + 36 -- scope" as opposed to the template call "local scope" of state items such + 37 -- as recursionDepth, curContext, and spliceMap. + 38 restoreState :: Monad m => TemplateState m -> TemplateMonad m () + 39 restoreState ts1 = + 40 modifyTS (\ts2 -> ts2 + 41 { _recursionDepth = _recursionDepth ts1 + 42 , _curContext = _curContext ts1 + 43 , _spliceMap = _spliceMap ts1 + 44 }) + 45 46 - 47 - 48 ------------------------------------------------------------------------------ - 49 type TemplateMap = Map TPath Template - 50 - 51 - 52 ------------------------------------------------------------------------------ - 53 -- | Holds all the state information needed for template processing: - 54 -- - 55 -- * a collection of named templates. If you use the @\<apply - 56 -- template=\"foo\"\>@ tag to include another template by name, @\"foo\"@ - 57 -- is looked up in here. - 58 -- - 59 -- * the mapping from tag names to 'Splice's. - 60 -- - 61 -- * a flag to control whether we will recurse during splice processing. - 62 -- - 63 -- We'll illustrate the recursion flag with a small example template: - 64 -- - 65 -- > <foo> - 66 -- > <bar> - 67 -- > ... - 68 -- > </bar> - 69 -- > </foo> - 70 -- - 71 -- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ - 72 -- splice will result in a list of nodes @L@; if the recursion flag is on we - 73 -- will recursively scan @L@ for splices, otherwise @L@ will be included in the - 74 -- output verbatim. - 75 data TemplateState m = TemplateState { - 76 -- | A mapping of splice names to splice actions - 77 _spliceMap :: SpliceMap m - 78 -- | A mapping of template names to templates - 79 , _templateMap :: TemplateMap - 80 -- | A flag to control splice recursion - 81 , _recurse :: Bool - 82 , _curContext :: TPath - 83 , _recursionDepth :: Int - 84 , _onLoadHook :: Template -> IO Template - 85 , _preRunHook :: Template -> m Template - 86 , _postRunHook :: Template -> m Template - 87 } - 88 - 89 - 90 ------------------------------------------------------------------------------ - 91 instance Eq (TemplateState m) where - 92 a == b = (_recurse a == _recurse b) && - 93 (_templateMap a == _templateMap b) && - 94 (_curContext a == _curContext b) + 47 ------------------------------------------------------------------------------ + 48 -- | Mappends a doctype to the state. + 49 addDoctype :: Monad m => [ByteString] -> TemplateMonad m () + 50 addDoctype dt = do + 51 modifyTS (\s -> s { _doctypes = _doctypes s `mappend` dt }) + 52 + 53 + 54 ------------------------------------------------------------------------------ + 55 -- TemplateState functions + 56 ------------------------------------------------------------------------------ + 57 + 58 + 59 ------------------------------------------------------------------------------ + 60 -- | Adds an on-load hook to a `TemplateState`. + 61 addOnLoadHook :: (Monad m) => + 62 (Template -> IO Template) + 63 -> TemplateState m + 64 -> TemplateState m + 65 addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } + 66 + 67 + 68 ------------------------------------------------------------------------------ + 69 -- | Adds a pre-run hook to a `TemplateState`. + 70 addPreRunHook :: (Monad m) => + 71 (Template -> m Template) + 72 -> TemplateState m + 73 -> TemplateState m + 74 addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } + 75 + 76 + 77 ------------------------------------------------------------------------------ + 78 -- | Adds a post-run hook to a `TemplateState`. + 79 addPostRunHook :: (Monad m) => + 80 (Template -> m Template) + 81 -> TemplateState m + 82 -> TemplateState m + 83 addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } + 84 + 85 + 86 ------------------------------------------------------------------------------ + 87 -- | Bind a new splice declaration to a tag name within a 'TemplateState'. + 88 bindSplice :: Monad m => + 89 ByteString -- ^ tag name + 90 -> Splice m -- ^ splice action + 91 -> TemplateState m -- ^ source state + 92 -> TemplateState m + 93 bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} + 94 95 - 96 - 97 ------------------------------------------------------------------------------ - 98 -- | 'TemplateMonad' is a monad transformer that gives you access to the 'Node' - 99 -- being processed (using the 'MonadReader' instance) as well as holding the - 100 -- 'TemplateState' that contains splice and template mappings (accessible - 101 -- using the 'MonadState' instance. - 102 newtype TemplateMonad m a = TemplateMonad (RWST Node () (TemplateState m) m a) - 103 deriving ( Monad - 104 , MonadIO - 105 , MonadCatchIO - 106 , MonadReader Node - 107 , MonadState (TemplateState m) ) - 108 - 109 - 110 ------------------------------------------------------------------------------ - 111 instance (Monad m) => Monoid (TemplateState m) where - 112 mempty = TemplateState Map.empty Map.empty True [] 0 - 113 return return return - 114 - 115 (TemplateState s1 t1 r1 _ d1 o1 b1 a1) `mappend` - 116 (TemplateState s2 t2 r2 c2 d2 o2 b2 a2) = - 117 TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) - 118 where - 119 s = s1 `mappend` s2 - 120 t = t1 `mappend` t2 - 121 r = r1 && r2 - 122 d = max d1 d2 - 123 - 124 - 125 ------------------------------------------------------------------------------ - 126 instance MonadTrans TemplateMonad where - 127 lift = TemplateMonad . lift + 96 ------------------------------------------------------------------------------ + 97 -- | Convenience function for looking up a splice. + 98 lookupSplice :: Monad m => + 99 ByteString + 100 -> TemplateState m + 101 -> Maybe (Splice m) + 102 lookupSplice nm ts = Map.lookup nm $ _spliceMap ts + 103 + 104 + 105 ------------------------------------------------------------------------------ + 106 -- | Converts a path into an array of the elements in reverse order. If the + 107 -- path is absolute, we need to remove the leading slash so the split doesn't + 108 -- leave @\"\"@ as the last element of the TPath. + 109 -- + 110 -- FIXME @\"..\"@ currently doesn't work in paths, the solution is non-trivial + 111 splitPathWith :: Char -> ByteString -> TPath + 112 splitPathWith s p = if B.null p then [] else (reverse $ B.split s path) + 113 where + 114 path = if B.head p == s then B.tail p else p + 115 + 116 -- | Converts a path into an array of the elements in reverse order using the + 117 -- path separator of the local operating system. See 'splitPathWith' for more + 118 -- details. + 119 splitLocalPath :: ByteString -> TPath + 120 splitLocalPath = splitPathWith pathSeparator + 121 + 122 -- | Converts a path into an array of the elements in reverse order using a + 123 -- forward slash (/) as the path separator. See 'splitPathWith' for more + 124 -- details. + 125 splitTemplatePath :: ByteString -> TPath + 126 splitTemplatePath = splitPathWith '/' + 127 128 129 ------------------------------------------------------------------------------ - 130 instance (Typeable1 m, Typeable a) => Typeable (TemplateMonad m a) where - 131 typeOf _ = mkTyConApp tCon [mRep, aRep] - 132 where - 133 tCon = mkTyCon "TemplateMonad" - 134 maRep = typeOf (undefined :: m a) - 135 (mCon, [aRep]) = splitTyConApp maRep - 136 mRep = mkTyConApp mCon [] + 130 -- | Does a single template lookup without cascading up. + 131 singleLookup :: TemplateMap + 132 -> TPath + 133 -> ByteString + 134 -> Maybe (InternalTemplate, TPath) + 135 singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm + 136 137 - 138 - 139 ------------------------------------------------------------------------------ - 140 -- | A Splice is a TemplateMonad computation that returns [Node]. - 141 type Splice m = TemplateMonad m Template - 142 - 143 - 144 ------------------------------------------------------------------------------ - 145 -- | SpliceMap associates a name and a Splice. - 146 type SpliceMap m = Map ByteString (Splice m) - 147 - 148 - 149 ------------------------------------------------------------------------------ - 150 -- TemplateState functions + 138 ------------------------------------------------------------------------------ + 139 -- | Searches for a template by looking in the full path then backing up into each + 140 -- of the parent directories until the template is found. + 141 traversePath :: TemplateMap + 142 -> TPath + 143 -> ByteString + 144 -> Maybe (InternalTemplate, TPath) + 145 traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) + 146 traversePath tm path name = + 147 singleLookup tm path name `mplus` + 148 traversePath tm (tail path) name + 149 + 150 151 ------------------------------------------------------------------------------ - 152 - 153 - 154 ------------------------------------------------------------------------------ - 155 -- | Adds an on-load hook to a `TemplateState`. - 156 addOnLoadHook :: (Monad m) => - 157 (Template -> IO Template) - 158 -> TemplateState m - 159 -> TemplateState m - 160 addOnLoadHook hook ts = ts { _onLoadHook = _onLoadHook ts >=> hook } - 161 - 162 - 163 ------------------------------------------------------------------------------ - 164 -- | Adds a pre-run hook to a `TemplateState`. - 165 addPreRunHook :: (Monad m) => - 166 (Template -> m Template) - 167 -> TemplateState m - 168 -> TemplateState m - 169 addPreRunHook hook ts = ts { _preRunHook = _preRunHook ts >=> hook } - 170 - 171 - 172 ------------------------------------------------------------------------------ - 173 -- | Adds a post-run hook to a `TemplateState`. - 174 addPostRunHook :: (Monad m) => - 175 (Template -> m Template) - 176 -> TemplateState m - 177 -> TemplateState m - 178 addPostRunHook hook ts = ts { _postRunHook = _postRunHook ts >=> hook } - 179 - 180 - 181 ------------------------------------------------------------------------------ - 182 -- | Bind a new splice declaration to a tag name within a 'TemplateState'. - 183 bindSplice :: Monad m => - 184 ByteString -- ^ tag name - 185 -> Splice m -- ^ splice action - 186 -> TemplateState m -- ^ source state - 187 -> TemplateState m - 188 bindSplice n v ts = ts {_spliceMap = Map.insert n v (_spliceMap ts)} - 189 - 190 - 191 ------------------------------------------------------------------------------ - 192 -- | Convenience function for looking up a splice. - 193 lookupSplice :: Monad m => - 194 ByteString - 195 -> TemplateState m - 196 -> Maybe (Splice m) - 197 lookupSplice nm ts = Map.lookup nm $ _spliceMap ts - 198 - 199 - 200 ------------------------------------------------------------------------------ - 201 -- | Converts a path into an array of the elements in reverse order. If the - 202 -- path is absolute, we need to remove the leading slash so the split doesn't - 203 -- leave @\"\"@ as the last element of the TPath. + 152 -- | Convenience function for looking up a template. + 153 lookupTemplate :: Monad m => + 154 ByteString + 155 -> TemplateState m + 156 -> Maybe (InternalTemplate, TPath) + 157 lookupTemplate nameStr ts = + 158 f (_templateMap ts) path name + 159 where (name:p) = case splitTemplatePath nameStr of + 160 [] -> [""] + 161 ps -> ps + 162 path = p ++ (_curContext ts) + 163 f = if '/' `B.elem` nameStr + 164 then singleLookup + 165 else traversePath + 166 + 167 + 168 ------------------------------------------------------------------------------ + 169 -- | Sets the templateMap in a TemplateState. + 170 setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m + 171 setTemplates m ts = ts { _templateMap = m } + 172 + 173 + 174 ------------------------------------------------------------------------------ + 175 -- | Adds a template to the template state. + 176 insertTemplate :: Monad m => + 177 TPath + 178 -> InternalTemplate + 179 -> TemplateState m + 180 -> TemplateState m + 181 insertTemplate p t st = + 182 setTemplates (Map.insert p t (_templateMap st)) st + 183 + 184 + 185 ------------------------------------------------------------------------------ + 186 -- | Adds a template to the template state. + 187 addTemplate :: Monad m => + 188 ByteString + 189 -> InternalTemplate + 190 -> TemplateState m + 191 -> TemplateState m + 192 addTemplate n t st = insertTemplate (splitTemplatePath n) t st + 193 + 194 + 195 ------------------------------------------------------------------------------ + 196 -- | Stops the recursive processing of splices. Consider the following + 197 -- example: + 198 -- + 199 -- > <foo> + 200 -- > <bar> + 201 -- > ... + 202 -- > </bar> + 203 -- > </foo> 204 -- - 205 -- FIXME @\"..\"@ currently doesn't work in paths, the solution is non-trivial - 206 splitPaths :: ByteString -> TPath - 207 splitPaths p = if B.null p then [] else (reverse $ B.split '/' path) - 208 where - 209 path = if B.head p == '/' then B.tail p else p - 210 + 205 -- Assume that @\"foo\"@ is bound to a splice procedure. Running the @foo@ + 206 -- splice will result in a list of nodes @L@. Normally @foo@ will recursively + 207 -- scan @L@ for splices and run them. If @foo@ calls @stopRecursion@, @L@ + 208 -- will be included in the output verbatim without running any splices. + 209 stopRecursion :: Monad m => TemplateMonad m () + 210 stopRecursion = modifyTS (\st -> st { _recurse = False }) 211 - 212 ------------------------------------------------------------------------------ - 213 -- | Does a single template lookup without cascading up. - 214 singleLookup :: TemplateMap - 215 -> TPath - 216 -> ByteString - 217 -> Maybe (Template, TPath) - 218 singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm - 219 - 220 - 221 ------------------------------------------------------------------------------ - 222 -- | Searches for a template by looking in the full path then backing up into each - 223 -- of the parent directories until the template is found. - 224 traversePath :: TemplateMap - 225 -> TPath - 226 -> ByteString - 227 -> Maybe (Template, TPath) - 228 traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) - 229 traversePath tm path name = - 230 singleLookup tm path name `mplus` - 231 traversePath tm (tail path) name - 232 - 233 - 234 ------------------------------------------------------------------------------ - 235 -- | Convenience function for looking up a template. - 236 lookupTemplate :: Monad m => - 237 ByteString - 238 -> TemplateState m - 239 -> Maybe (Template, TPath) - 240 lookupTemplate nameStr ts = - 241 f (_templateMap ts) path name - 242 where (name:p) = case splitPaths nameStr of - 243 [] -> [""] - 244 ps -> ps - 245 path = p ++ (_curContext ts) - 246 f = if '/' `B.elem` nameStr - 247 then singleLookup - 248 else traversePath - 249 - 250 - 251 ------------------------------------------------------------------------------ - 252 -- | Sets the templateMap in a TemplateState. - 253 setTemplates :: Monad m => TemplateMap -> TemplateState m -> TemplateState m - 254 setTemplates m ts = ts { _templateMap = m } - 255 - 256 - 257 ------------------------------------------------------------------------------ - 258 -- | Adds a template to the template state. - 259 insertTemplate :: Monad m => - 260 TPath - 261 -> Template - 262 -> TemplateState m - 263 -> TemplateState m - 264 insertTemplate p t st = - 265 setTemplates (Map.insert p t (_templateMap st)) st - 266 - 267 - 268 ------------------------------------------------------------------------------ - 269 -- | Adds a template to the template state. - 270 addTemplate :: Monad m => - 271 ByteString - 272 -> Template - 273 -> TemplateState m - 274 -> TemplateState m - 275 addTemplate n t st = insertTemplate (splitPaths n) t st - 276 - 277 - 278 ------------------------------------------------------------------------------ - 279 -- | Gets the node currently being processed. - 280 getParamNode :: Monad m => TemplateMonad m Node - 281 getParamNode = ask - 282 + 212 + 213 ------------------------------------------------------------------------------ + 214 -- | Sets the current context + 215 setContext :: Monad m => TPath -> TemplateMonad m () + 216 setContext c = modifyTS (\st -> st { _curContext = c }) + 217 + 218 + 219 ------------------------------------------------------------------------------ + 220 -- | Gets the current context + 221 getContext :: Monad m => TemplateMonad m TPath + 222 getContext = getsTS _curContext + 223 + 224 + 225 ------------------------------------------------------------------------------ + 226 -- | Performs splice processing on a single node. + 227 runNode :: Monad m => Node -> Splice m + 228 runNode n@(X.Text _) = return [n] + 229 runNode n@(X.Element nm at ch) = do + 230 s <- liftM (lookupSplice nm) getTS + 231 maybe runChildren (recurseSplice n) s + 232 + 233 where + 234 runChildren = do + 235 newKids <- runNodeList ch + 236 newAtts <- mapM attSubst at + 237 return [X.Element nm newAtts newKids] + 238 + 239 + 240 ------------------------------------------------------------------------------ + 241 -- | Helper function for substituting a parsed attribute into an attribute + 242 -- tuple. + 243 attSubst :: (Monad m) => (t, ByteString) -> TemplateMonad m (t, ByteString) + 244 attSubst (n,v) = do + 245 v' <- parseAtt v + 246 return (n,v') + 247 + 248 + 249 ------------------------------------------------------------------------------ + 250 -- | Parses an attribute for any identifier expressions and performs + 251 -- appropriate substitution. + 252 parseAtt :: (Monad m) => ByteString -> TemplateMonad m ByteString + 253 parseAtt bs = do + 254 let ast = case AP.feed (AP.parse attParser bs) "" of + 255 (AP.Fail _ _ _) -> [] + 256 (AP.Done _ res) -> res + 257 (AP.Partial _) -> [] + 258 chunks <- mapM cvt ast + 259 return $ B.concat chunks + 260 where + 261 cvt (Literal x) = return x + 262 cvt (Ident x) = getAttributeSplice x + 263 + 264 + 265 ------------------------------------------------------------------------------ + 266 -- | AST to hold attribute parsing structure. This is necessary because + 267 -- attoparsec doesn't support parsers running in another monad. + 268 data AttAST = Literal ByteString | + 269 Ident ByteString + 270 deriving (Show) + 271 + 272 + 273 ------------------------------------------------------------------------------ + 274 -- | Parser for attribute variable substitution. + 275 attParser :: AP.Parser [AttAST] + 276 attParser = AP.many1 (identParser <|> litParser) + 277 where + 278 escChar = (AP.char '\\' *> AP.anyChar) <|> + 279 AP.satisfy (AP.notInClass "\\$") + 280 litParser = Literal <$> (B.pack <$> AP.many1 escChar) + 281 identParser = AP.string "$(" *> + 282 (Ident <$> AP.takeWhile (/=')')) <* AP.string ")" 283 - 284 ------------------------------------------------------------------------------ - 285 -- | Stops the recursive processing of splices. - 286 stopRecursion :: Monad m => TemplateMonad m () - 287 stopRecursion = modify (\st -> st { _recurse = False }) - 288 - 289 - 290 ------------------------------------------------------------------------------ - 291 -- | Sets the current context - 292 setContext :: Monad m => TPath -> TemplateMonad m () - 293 setContext c = modify (\st -> st { _curContext = c }) - 294 - 295 - 296 ------------------------------------------------------------------------------ - 297 -- | Gets the current context - 298 getContext :: Monad m => TemplateMonad m TPath - 299 getContext = gets _curContext - 300 - 301 - 302 ------------------------------------------------------------------------------ - 303 -- | Performs splice processing on a list of nodes. - 304 runNodeList :: Monad m => [Node] -> Splice m - 305 runNodeList nodes = liftM concat $ sequence (map runNode nodes) - 306 - 307 - 308 ------------------------------------------------------------------------------ - 309 -- | Performs splice processing on a single node. - 310 runNode :: Monad m => Node -> Splice m - 311 runNode n@(X.Text _) = return [n] - 312 runNode n@(X.Element nm _ ch) = do - 313 s <- liftM (lookupSplice nm) get - 314 maybe runChildren (recurseSplice n) s - 315 - 316 where - 317 runChildren = do - 318 newKids <- runNodeList ch - 319 return [X.modifyChildren (const newKids) n] - 320 - 321 - 322 ------------------------------------------------------------------------------ - 323 -- | The maximum recursion depth. (Used to prevent infinite loops.) - 324 mAX_RECURSION_DEPTH :: Int - 325 mAX_RECURSION_DEPTH = 20 - 326 + 284 + 285 ------------------------------------------------------------------------------ + 286 -- | Get's the attribute value. This is just a normal splice lookup with the + 287 -- added restriction that the splice's result list has to contain a single + 288 -- text element. Otherwise the attribute evaluates to the empty string. + 289 getAttributeSplice :: Monad m => ByteString -> TemplateMonad m ByteString + 290 getAttributeSplice name = do + 291 s <- liftM (lookupSplice name) getTS + 292 nodes <- maybe (return []) id s + 293 return $ check nodes + 294 where + 295 check ((X.Text t):_) = t + 296 check _ = "" + 297 + 298 ------------------------------------------------------------------------------ + 299 -- | Performs splice processing on a list of nodes. + 300 runNodeList :: Monad m => [Node] -> Splice m + 301 runNodeList nodes = liftM concat $ sequence (map runNode nodes) + 302 + 303 + 304 ------------------------------------------------------------------------------ + 305 -- | The maximum recursion depth. (Used to prevent infinite loops.) + 306 mAX_RECURSION_DEPTH :: Int + 307 mAX_RECURSION_DEPTH = 50 + 308 + 309 + 310 ------------------------------------------------------------------------------ + 311 -- | Checks the recursion flag and recurses accordingly. Does not recurse + 312 -- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. + 313 recurseSplice :: Monad m => Node -> Splice m -> Splice m + 314 recurseSplice node splice = do + 315 result <- localParamNode (const node) splice + 316 ts' <- getTS + 317 if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH + 318 then do modRecursionDepth (+1) + 319 res <- runNodeList result + 320 restoreState ts' + 321 return res + 322 else return result + 323 where + 324 modRecursionDepth :: Monad m => (Int -> Int) -> TemplateMonad m () + 325 modRecursionDepth f = + 326 modifyTS (\st -> st { _recursionDepth = f (_recursionDepth st) }) 327 - 328 ------------------------------------------------------------------------------ - 329 -- | Checks the recursion flag and recurses accordingly. Does not recurse - 330 -- deeper than mAX_RECURSION_DEPTH to avoid infinite loops. - 331 recurseSplice :: Monad m => Node -> Splice m -> Splice m - 332 recurseSplice node splice = do - 333 result <- local (const node) splice - 334 ts' <- get - 335 if _recurse ts' && _recursionDepth ts' < mAX_RECURSION_DEPTH - 336 then do modify (\st -> st { _recursionDepth = _recursionDepth st + 1 }) - 337 res <- runNodeList result - 338 put ts' - 339 return res - 340 else return result - 341 - 342 - 343 ------------------------------------------------------------------------------ - 344 -- | Runs a splice in the underlying monad. Splices require two - 345 -- parameters, the template state, and an input node. - 346 runSplice :: Monad m => - 347 TemplateState m -- ^ The initial template state - 348 -> Node -- ^ The splice's input node - 349 -> Splice m -- ^ The splice - 350 -> m [Node] - 351 runSplice ts node (TemplateMonad splice) = do - 352 (result,_,_) <- runRWST splice node ts - 353 return result + 328 + 329 ------------------------------------------------------------------------------ + 330 -- | Looks up a template name runs a TemplateMonad computation on it. + 331 lookupAndRun :: Monad m + 332 => ByteString + 333 -> ((InternalTemplate, TPath) -> TemplateMonad m (Maybe a)) + 334 -> TemplateMonad m (Maybe a) + 335 lookupAndRun name k = do + 336 ts <- getTS + 337 maybe (return Nothing) k + 338 (lookupTemplate name ts) + 339 + 340 + 341 ------------------------------------------------------------------------------ + 342 -- | Looks up a template name evaluates it by calling runNodeList. + 343 evalTemplate :: Monad m + 344 => ByteString + 345 -> TemplateMonad m (Maybe Template) + 346 evalTemplate name = lookupAndRun name + 347 (\(t,ctx) -> do + 348 ts <- getTS + 349 putTS (ts {_curContext = ctx}) + 350 res <- runNodeList $ _itNodes t + 351 restoreState ts + 352 return $ Just res) + 353 354 - 355 - 356 ------------------------------------------------------------------------------ - 357 -- | Runs a template in the underlying monad. Similar to runSplice - 358 -- except that templates don't require a Node as a parameter. - 359 runRawTemplate :: Monad m => TemplateState m -> Template -> m [Node] - 360 runRawTemplate ts template = - 361 _preRunHook ts template >>= - 362 runSplice ts (X.Text "") . runNodeList >>= - 363 _postRunHook ts - 364 - 365 - 366 ------------------------------------------------------------------------------ - 367 -- | Looks up a template name in the supplied 'TemplateState' and runs - 368 -- it in the underlying monad. - 369 runTemplate :: Monad m - 370 => TemplateState m - 371 -> ByteString - 372 -> m (Maybe [Node]) - 373 runTemplate ts name = - 374 maybe (return Nothing) - 375 (\(t,ctx) -> - 376 return . Just =<< - 377 runRawTemplate (ts {_curContext = ctx}) t) - 378 (lookupTemplate name ts) - 379 - 380 - 381 ------------------------------------------------------------------------------ - 382 -- | Looks up a template name evaluates it. Same as runTemplate except it - 383 -- runs in TemplateMonad instead of m. - 384 evalTemplate :: Monad m - 385 => ByteString - 386 -> TemplateMonad m (Maybe [Node]) - 387 evalTemplate name = do - 388 ts <- get - 389 lift $ runTemplate ts name - 390 - 391 - 392 ------------------------------------------------------------------------------ - 393 -- | Binds a list of constant string splices - 394 bindStrings :: Monad m - 395 => [(ByteString, ByteString)] - 396 -> TemplateState m - 397 -> TemplateState m - 398 bindStrings pairs ts = foldr add ts pairs - 399 where - 400 add (n,v) = bindSplice n (return [X.Text v]) - 401 - 402 - 403 ------------------------------------------------------------------------------ - 404 -- | Renders a template with the specified parameters. This is the function - 405 -- to use when you want to "call" a template and pass in parameters from code. - 406 callTemplate :: Monad m - 407 => ByteString -- ^ The name of the template - 408 -> [(ByteString, ByteString)] -- ^ Association list of - 409 -- (name,value) parameter pairs - 410 -> TemplateMonad m (Maybe Template) - 411 callTemplate name params = do - 412 modify $ bindStrings params - 413 evalTemplate name - 414 + 355 ------------------------------------------------------------------------------ + 356 -- | Looks up a template name evaluates it by calling runNodeList. This also + 357 -- executes pre- and post-run hooks and adds the doctype. + 358 evalWithHooks :: Monad m + 359 => ByteString + 360 -> TemplateMonad m (Maybe Template) + 361 evalWithHooks name = lookupAndRun name + 362 (\(t,ctx) -> do + 363 addDoctype $ maybeToList $ _itDoctype t + 364 ts <- getTS + 365 nodes <- lift $ _preRunHook ts $ _itNodes t + 366 putTS (ts {_curContext = ctx}) + 367 res <- runNodeList nodes + 368 restoreState ts + 369 return . Just =<< lift (_postRunHook ts res)) + 370 + 371 + 372 ------------------------------------------------------------------------------ + 373 -- | Binds a list of constant string splices + 374 bindStrings :: Monad m + 375 => [(ByteString, ByteString)] + 376 -> TemplateState m + 377 -> TemplateState m + 378 bindStrings pairs ts = foldr add ts pairs + 379 where + 380 add (n,v) = bindSplice n (return [X.Text v]) + 381 + 382 + 383 ------------------------------------------------------------------------------ + 384 -- | Renders a template with the specified parameters. This is the function + 385 -- to use when you want to "call" a template and pass in parameters from code. + 386 callTemplate :: Monad m + 387 => ByteString -- ^ The name of the template + 388 -> [(ByteString, ByteString)] -- ^ Association list of + 389 -- (name,value) parameter pairs + 390 -> TemplateMonad m (Maybe Template) + 391 callTemplate name params = do + 392 modifyTS $ bindStrings params + 393 evalTemplate name + 394 + 395 + 396 ------------------------------------------------------------------------------ + 397 -- | Converts a Template to an InternalTemplate. This can only be done inside + 398 -- TemplateMonad where the doctype is available. + 399 toInternalTemplate :: Monad m => Template -> TemplateMonad m InternalTemplate + 400 toInternalTemplate t = do + 401 dts <- getsTS _doctypes + 402 return $ InternalTemplate { + 403 _itDoctype = listToMaybe dts, + 404 _itNodes = t + 405 } + 406 + 407 + 408 ------------------------------------------------------------------------------ + 409 -- | Renders an internal template by prepending the appropriate doctype. + 410 renderInternal :: Monad m => InternalTemplate -> TemplateMonad m ByteString + 411 renderInternal (InternalTemplate dt nodes) = + 412 return $ maybe bs (flip B.append bs) dt + 413 where + 414 bs = formatList' nodes 415 - 416 ------------------------------------------------------------------------------ - 417 -- | Renders a template from the specified TemplateState. - 418 renderTemplate :: Monad m - 419 => TemplateState m - 420 -> ByteString - 421 -> m (Maybe ByteString) - 422 renderTemplate ts name = do - 423 ns <- runTemplate ts name - 424 return $ (Just . formatList') =<< ns - 425 - 426 - 427 ------------------------------------------------------------------------------ - 428 heistExpatOptions :: X.ParserOptions ByteString ByteString - 429 heistExpatOptions = - 430 X.defaultParserOptions { - 431 X.parserEncoding = Just X.UTF8 - 432 , X.entityDecoder = Just (\k -> Map.lookup k htmlEntityLookupTable) - 433 } + 416 + 417 ------------------------------------------------------------------------------ + 418 -- | Renders a template from the specified TemplateState. + 419 renderTemplate :: Monad m + 420 => TemplateState m + 421 -> ByteString + 422 -> m (Maybe ByteString) + 423 renderTemplate ts name = do + 424 evalTemplateMonad + 425 (do mt <- evalWithHooks name + 426 maybe (return Nothing) + 427 (\t -> liftM Just $ renderInternal =<< toInternalTemplate t) + 428 mt + 429 ) (X.Text "") ts + 430 + 431 ------------------------------------------------------------------------------ + 432 -- Template loading + 433 ------------------------------------------------------------------------------ 434 - 435 ------------------------------------------------------------------------------ - 436 -- Template loading - 437 ------------------------------------------------------------------------------ - 438 - 439 -- | Reads an XML document from disk. - 440 getDoc :: String -> IO (Either String Template) - 441 getDoc f = do - 442 bs <- catch (liftM Right $ B.readFile f) - 443 (\(e::SomeException) -> return $ Left $ show e) - 444 let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" - 445 return $ (mapRight X.getChildren . - 446 mapLeft genErrorMsg . - 447 X.parse' heistExpatOptions . wrap) =<< bs - 448 where - 449 genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str - 450 locMsg (X.XMLParseLocation line col _ _) = - 451 "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" - 452 translate "junk after document element" = "document must have a single root element" - 453 translate s = s - 454 - 455 ------------------------------------------------------------------------------ - 456 mapLeft :: (a -> b) -> Either a c -> Either b c - 457 mapLeft g = either (Left . g) Right - 458 mapRight :: (b -> c) -> Either a b -> Either a c - 459 mapRight g = either Left (Right . g) - 460 - 461 - 462 ------------------------------------------------------------------------------ - 463 -- | Loads a template with the specified path and filename. The - 464 -- template is only loaded if it has a ".tpl" extension. - 465 loadTemplate :: String -- ^ path of the template root - 466 -> String -- ^ full file path (includes the template root) - 467 -> IO [Either String (TPath, Template)] --TemplateMap - 468 loadTemplate templateRoot fname - 469 | ".tpl" `isSuffixOf` fname = do - 470 c <- getDoc fname - 471 return [fmap (\t -> (splitPaths $ B.pack tName, t)) c] - 472 | otherwise = return [] - 473 where -- tName is path relative to the template root directory - 474 tName = drop ((length templateRoot)+1) $ - 475 -- We're only dropping the template root, not the whole path - 476 take ((length fname) - 4) fname - 477 - 478 - 479 ------------------------------------------------------------------------------ - 480 -- | Traverses the specified directory structure and builds a - 481 -- TemplateState by loading all the files with a ".tpl" extension. - 482 loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) - 483 loadTemplates dir ts = do - 484 d <- readDirectoryWith (loadTemplate dir) dir - 485 let tlist = F.fold (free d) - 486 errs = lefts tlist - 487 case errs of - 488 [] -> liftM Right $ foldM loadHook ts $ rights tlist - 489 _ -> return $ Left $ unlines errs - 490 - 491 - 492 ------------------------------------------------------------------------------ - 493 -- | Runs the onLoad hook on the template and returns the `TemplateState` - 494 -- with the result inserted. - 495 loadHook :: Monad m => TemplateState m -> (TPath, Template) -> IO (TemplateState m) - 496 loadHook ts (tp, t) = do - 497 t' <- _onLoadHook ts t - 498 return $ insertTemplate tp t' ts + 435 -- | Reads an XML document from disk. + 436 getDoc :: String -> IO (Either String InternalTemplate) + 437 getDoc f = do + 438 bs <- catch (liftM Right $ B.readFile f) + 439 (\(e::SomeException) -> return $ Left $ show e) + 440 return $ do + 441 (doctype, rest) <- liftM extractDoctype bs + 442 let wrap b = "<snap:root>\n" `B.append` b `B.append` "\n</snap:root>" + 443 toTemplate t = InternalTemplate { + 444 _itDoctype = doctype, + 445 _itNodes = t + 446 } + 447 mapRight (toTemplate . X.getChildren) . + 448 mapLeft genErrorMsg . + 449 X.parse' heistExpatOptions . wrap $ rest + 450 where + 451 genErrorMsg (X.XMLParseError str loc) = f ++ " " ++ locMsg loc ++ ": " ++ translate str + 452 locMsg (X.XMLParseLocation line col _ _) = + 453 "(line " ++ show (line-1) ++ ", col " ++ show col ++ ")" + 454 translate "junk after document element" = "document must have a single root element" + 455 translate s = s + 456 + 457 + 458 ------------------------------------------------------------------------------ + 459 -- | Checks whether the bytestring has a doctype. + 460 hasDoctype :: ByteString -> Bool + 461 hasDoctype bs = "<!DOCTYPE" `B.isPrefixOf` bs + 462 + 463 + 464 ------------------------------------------------------------------------------ + 465 -- | Converts a ByteString into a tuple containing a possible doctype + 466 -- ByteString and the rest of the document. + 467 extractDoctype :: ByteString -> (Maybe ByteString, ByteString) + 468 extractDoctype bs = + 469 if hasDoctype bs + 470 then (Just $ B.snoc (B.takeWhile p bs) '>', B.tail $ B.dropWhile p bs) + 471 else (Nothing, bs) + 472 where + 473 p = (/='>') + 474 + 475 ------------------------------------------------------------------------------ + 476 mapLeft :: (a -> b) -> Either a c -> Either b c + 477 mapLeft g = either (Left . g) Right + 478 mapRight :: (b -> c) -> Either a b -> Either a c + 479 mapRight g = either Left (Right . g) + 480 + 481 + 482 ------------------------------------------------------------------------------ + 483 -- | Loads a template with the specified path and filename. The + 484 -- template is only loaded if it has a ".tpl" extension. + 485 loadTemplate :: String -- ^ path of the template root + 486 -> String -- ^ full file path (includes the template root) + 487 -> IO [Either String (TPath, InternalTemplate)] --TemplateMap + 488 loadTemplate templateRoot fname + 489 | ".tpl" `isSuffixOf` fname = do + 490 c <- getDoc fname + 491 return [fmap (\t -> (splitLocalPath $ B.pack tName, t)) c] + 492 | otherwise = return [] + 493 where -- tName is path relative to the template root directory + 494 correction = if last templateRoot == '/' then 0 else 1 + 495 tName = drop ((length templateRoot)+correction) $ + 496 -- We're only dropping the template root, not the whole path + 497 take ((length fname) - 4) fname + 498 499 - 500 - 501 ------------------------------------------------------------------------------ - 502 -- These are here until we can get them into hexpat. - 503 ------------------------------------------------------------------------------ - 504 - 505 formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => - 506 [X.Node tag text] - 507 -> L.ByteString - 508 formatList nodes = foldl L.append L.empty $ map formatNode nodes - 509 - 510 formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => - 511 [X.Node tag text] - 512 -> B.ByteString - 513 formatList' = B.concat . L.toChunks . formatList - 514 + 500 ------------------------------------------------------------------------------ + 501 -- | Traverses the specified directory structure and builds a + 502 -- TemplateState by loading all the files with a ".tpl" extension. + 503 loadTemplates :: Monad m => FilePath -> TemplateState m -> IO (Either String (TemplateState m)) + 504 loadTemplates dir ts = do + 505 d <- readDirectoryWith (loadTemplate dir) dir + 506 let tlist = F.fold (free d) + 507 errs = lefts tlist + 508 case errs of + 509 [] -> liftM Right $ foldM loadHook ts $ rights tlist + 510 _ -> return $ Left $ unlines errs + 511 + 512 + 513 ------------------------------------------------------------------------------ + 514 -- | Reversed list of directories. This holds the path to the template + 515 runHookInternal :: Monad m => (Template -> m Template) + 516 -> InternalTemplate + 517 -> m InternalTemplate + 518 runHookInternal f t = do + 519 n <- f $ _itNodes t + 520 return $ t { _itNodes = n } + 521 + 522 + 523 ------------------------------------------------------------------------------ + 524 -- | Runs the onLoad hook on the template and returns the `TemplateState` + 525 -- with the result inserted. + 526 loadHook :: Monad m => TemplateState m -> (TPath, InternalTemplate) -> IO (TemplateState m) + 527 loadHook ts (tp, t) = do + 528 t' <- runHookInternal (_onLoadHook ts) t + 529 return $ insertTemplate tp t' ts + 530 + 531 + 532 ------------------------------------------------------------------------------ + 533 -- These are here until we can get them into hexpat. + 534 ------------------------------------------------------------------------------ + 535 + 536 formatList :: (X.GenericXMLString tag, X.GenericXMLString text) => + 537 [X.Node tag text] + 538 -> L.ByteString + 539 formatList nodes = foldl L.append L.empty $ map formatNode nodes + 540 + 541 formatList' :: (X.GenericXMLString tag, X.GenericXMLString text) => + 542 [X.Node tag text] + 543 -> B.ByteString + 544 formatList' = B.concat . L.toChunks . formatList + 545 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Apply.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Apply.hs.html index bfe3909..6336bab 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Apply.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Apply.hs.html @@ -17,45 +17,49 @@ 6 import Control.Monad.RWS.Strict 7 import Data.ByteString.Char8 (ByteString) 8 import qualified Data.ByteString.Char8 as B - 9 import qualified Text.XML.Expat.Tree as X - 10 - 11 ------------------------------------------------------------------------------ - 12 import Text.Templating.Heist.Internal - 13 - 14 ------------------------------------------------------------------------------ - 15 -- | Default name for the apply splice. - 16 applyTag :: ByteString - 17 applyTag = "apply" - 18 - 19 - 20 ------------------------------------------------------------------------------ - 21 -- | Default attribute name for the apply tag. - 22 applyAttr :: ByteString - 23 applyAttr = "template" - 24 - 25 - 26 ------------------------------------------------------------------------------ - 27 -- | Implementation of the apply splice. - 28 applyImpl :: Monad m => Splice m - 29 applyImpl = do - 30 node <- getParamNode - 31 case X.getAttribute node applyAttr of - 32 Nothing -> return [] -- TODO: error handling - 33 Just attr -> do - 34 st <- get - 35 processedChildren <- runNodeList $ X.getChildren node - 36 modify (bindSplice "content" $ return processedChildren) - 37 maybe (return []) -- TODO: error handling - 38 (\(t,ctx) -> do setContext ctx - 39 result <- runNodeList t - 40 put st - 41 return result) - 42 (lookupTemplate attr (st {_curContext = nextCtx attr st})) - 43 where nextCtx name st - 44 | B.isPrefixOf "/" name = [] - 45 | otherwise = _curContext st - 46 - 47 + 9 import Data.Maybe + 10 import qualified Text.XML.Expat.Tree as X + 11 + 12 ------------------------------------------------------------------------------ + 13 import Text.Templating.Heist.Internal + 14 import Text.Templating.Heist.Types + 15 + 16 ------------------------------------------------------------------------------ + 17 -- | Default name for the apply splice. + 18 applyTag :: ByteString + 19 applyTag = "apply" + 20 + 21 + 22 ------------------------------------------------------------------------------ + 23 -- | Default attribute name for the apply tag. + 24 applyAttr :: ByteString + 25 applyAttr = "template" + 26 + 27 + 28 ------------------------------------------------------------------------------ + 29 -- | Implementation of the apply splice. + 30 applyImpl :: Monad m => Splice m + 31 applyImpl = do + 32 node <- getParamNode + 33 case X.getAttribute node applyAttr of + 34 Nothing -> return [] -- TODO: error handling + 35 Just attr -> do + 36 st <- getTS + 37 maybe (return []) -- TODO: error handling + 38 (\(t,ctx) -> do + 39 addDoctype $ maybeToList $ _itDoctype t + 40 processedChildren <- runNodeList $ X.getChildren node + 41 modifyTS (bindSplice "content" $ return processedChildren) + 42 setContext ctx + 43 result <- runNodeList $ _itNodes t + 44 restoreState st + 45 return result) + 46 (lookupTemplate attr (st {_curContext = nextCtx attr st})) + 47 where nextCtx name st + 48 | B.isPrefixOf "/" name = [] + 49 | otherwise = _curContext st + 50 + 51 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Bind.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Bind.hs.html index 43c6bf5..131d3b7 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Bind.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Bind.hs.html @@ -20,32 +20,33 @@ 9 10 ------------------------------------------------------------------------------ 11 import Text.Templating.Heist.Internal - 12 - 13 -- | Default name for the bind splice. - 14 bindTag :: ByteString - 15 bindTag = "bind" - 16 + 12 import Text.Templating.Heist.Types + 13 + 14 -- | Default name for the bind splice. + 15 bindTag :: ByteString + 16 bindTag = "bind" 17 - 18 ------------------------------------------------------------------------------ - 19 -- | Default attribute name for the bind tag. - 20 bindAttr :: ByteString - 21 bindAttr = "tag" - 22 + 18 + 19 ------------------------------------------------------------------------------ + 20 -- | Default attribute name for the bind tag. + 21 bindAttr :: ByteString + 22 bindAttr = "tag" 23 - 24 ------------------------------------------------------------------------------ - 25 -- | Implementation of the bind splice. - 26 bindImpl :: Monad m => Splice m - 27 bindImpl = do - 28 node <- getParamNode - 29 maybe (return ()) - 30 (add node) - 31 (X.getAttribute node bindAttr) - 32 return [] - 33 - 34 where - 35 add node nm = modify $ bindSplice nm (return $ X.getChildren node) - 36 + 24 + 25 ------------------------------------------------------------------------------ + 26 -- | Implementation of the bind splice. + 27 bindImpl :: Monad m => Splice m + 28 bindImpl = do + 29 node <- getParamNode + 30 maybe (return ()) + 31 (add node) + 32 (X.getAttribute node bindAttr) + 33 return [] + 34 + 35 where + 36 add node nm = modifyTS $ bindSplice nm (return $ X.getChildren node) 37 + 38 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Ignore.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Ignore.hs.html index 87582cf..86fc17d 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Ignore.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Ignore.hs.html @@ -17,7 +17,7 @@ 6 import Data.ByteString.Char8 (ByteString) 7 8 ------------------------------------------------------------------------------ - 9 import Text.Templating.Heist.Internal + 9 import Text.Templating.Heist.Types 10 11 12 ------------------------------------------------------------------------------ diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Markdown.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Markdown.hs.html index ffc1d04..6090300 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Markdown.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Markdown.hs.html @@ -13,152 +13,155 @@ 2 3 module Text.Templating.Heist.Splices.Markdown where 4 - 5 import Data.ByteString (ByteString) - 6 import qualified Data.ByteString as B - 7 import qualified Data.ByteString.Char8 as BC - 8 import Data.Maybe - 9 import Control.Concurrent - 10 import Control.Exception (throwIO) - 11 import Control.Monad - 12 import Control.Monad.CatchIO - 13 import Control.Monad.Trans - 14 import Data.Typeable - 15 import Prelude hiding (catch) - 16 import System.Directory - 17 import System.Exit - 18 import System.IO - 19 import System.Process - 20 import Text.Templating.Heist.Internal + 5 ------------------------------------------------------------------------------ + 6 import Data.ByteString (ByteString) + 7 import qualified Data.ByteString as B + 8 import qualified Data.ByteString.Char8 as BC + 9 import Data.Maybe + 10 import Control.Concurrent + 11 import Control.Exception (throwIO) + 12 import Control.Monad + 13 import Control.Monad.CatchIO + 14 import Control.Monad.Trans + 15 import Data.Typeable + 16 import Prelude hiding (catch) + 17 import System.Directory + 18 import System.Exit + 19 import System.IO + 20 import System.Process 21 import Text.XML.Expat.Tree hiding (Node) 22 - 23 - 24 data PandocMissingException = PandocMissingException - 25 deriving (Typeable) + 23 ------------------------------------------------------------------------------ + 24 import Text.Templating.Heist.Constants + 25 import Text.Templating.Heist.Types 26 - 27 instance Show PandocMissingException where - 28 show PandocMissingException = - 29 "Cannot find the \"pandoc\" executable; is it on your $PATH?" - 30 - 31 instance Exception PandocMissingException - 32 + 27 data PandocMissingException = PandocMissingException + 28 deriving (Typeable) + 29 + 30 instance Show PandocMissingException where + 31 show PandocMissingException = + 32 "Cannot find the \"pandoc\" executable; is it on your $PATH?" 33 - 34 data MarkdownException = MarkdownException ByteString - 35 deriving (Typeable) + 34 instance Exception PandocMissingException + 35 36 - 37 instance Show MarkdownException where - 38 show (MarkdownException e) = - 39 "Markdown error: pandoc replied:\n\n" ++ BC.unpack e - 40 - 41 instance Exception MarkdownException - 42 + 37 data MarkdownException = MarkdownException ByteString + 38 deriving (Typeable) + 39 + 40 instance Show MarkdownException where + 41 show (MarkdownException e) = + 42 "Markdown error: pandoc replied:\n\n" ++ BC.unpack e 43 - 44 ------------------------------------------------------------------------------ - 45 -- | Default name for the markdown splice. - 46 markdownTag :: ByteString - 47 markdownTag = "markdown" - 48 - 49 ------------------------------------------------------------------------------ - 50 -- | Implementation of the markdown splice. - 51 markdownSplice :: MonadIO m => Splice m - 52 markdownSplice = do - 53 pdMD <- liftIO $ findExecutable "pandoc" - 54 - 55 when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException - 56 - 57 tree <- getParamNode - 58 markup <- liftIO $ - 59 case getAttribute tree "file" of - 60 Just f -> pandoc (fromJust pdMD) $ BC.unpack f - 61 Nothing -> pandocBS (fromJust pdMD) $ textContent tree - 62 - 63 let ee = parse' heistExpatOptions markup - 64 case ee of - 65 (Left e) -> throw $ MarkdownException - 66 $ BC.pack ("Error parsing markdown output: " ++ show e) - 67 (Right n) -> return [n] - 68 - 69 - 70 pandoc :: FilePath -> FilePath -> IO ByteString - 71 pandoc pandocPath inputFile = do - 72 (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" - 73 - 74 when (isFail ex) $ throw $ MarkdownException serr - 75 return $ BC.concat [ "<div class=\"markdown\">\n" - 76 , sout - 77 , "\n</div>" ] - 78 - 79 where - 80 isFail ExitSuccess = False - 81 isFail _ = True - 82 - 83 -- FIXME: hardcoded path - 84 args = [ "-S", "--no-wrap", "templates/"++inputFile ] - 85 - 86 - 87 pandocBS :: FilePath -> ByteString -> IO ByteString - 88 pandocBS pandocPath s = do - 89 -- using the crummy string functions for convenience here - 90 (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s - 91 - 92 when (isFail ex) $ throw $ MarkdownException serr - 93 return $ BC.concat [ "<div class=\"markdown\">\n" - 94 , sout - 95 , "\n</div>" ] - 96 - 97 where - 98 isFail ExitSuccess = False - 99 isFail _ = True - 100 args = [ "-S", "--no-wrap" ] - 101 - 102 - 103 -- a version of readProcessWithExitCode that does I/O properly - 104 readProcessWithExitCode' - 105 :: FilePath -- ^ command to run - 106 -> [String] -- ^ any arguments - 107 -> ByteString -- ^ standard input - 108 -> IO (ExitCode,ByteString,ByteString) -- ^ exitcode, stdout, stderr - 109 readProcessWithExitCode' cmd args input = do - 110 (Just inh, Just outh, Just errh, pid) <- - 111 createProcess (proc cmd args){ std_in = CreatePipe, - 112 std_out = CreatePipe, - 113 std_err = CreatePipe } - 114 outMVar <- newEmptyMVar - 115 - 116 outM <- newEmptyMVar - 117 errM <- newEmptyMVar + 44 instance Exception MarkdownException + 45 + 46 + 47 ------------------------------------------------------------------------------ + 48 -- | Default name for the markdown splice. + 49 markdownTag :: ByteString + 50 markdownTag = "markdown" + 51 + 52 ------------------------------------------------------------------------------ + 53 -- | Implementation of the markdown splice. + 54 markdownSplice :: MonadIO m => Splice m + 55 markdownSplice = do + 56 pdMD <- liftIO $ findExecutable "pandoc" + 57 + 58 when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException + 59 + 60 tree <- getParamNode + 61 markup <- liftIO $ + 62 case getAttribute tree "file" of + 63 Just f -> pandoc (fromJust pdMD) $ BC.unpack f + 64 Nothing -> pandocBS (fromJust pdMD) $ textContent tree + 65 + 66 let ee = parse' heistExpatOptions markup + 67 case ee of + 68 (Left e) -> throw $ MarkdownException + 69 $ BC.pack ("Error parsing markdown output: " ++ show e) + 70 (Right n) -> return [n] + 71 + 72 + 73 pandoc :: FilePath -> FilePath -> IO ByteString + 74 pandoc pandocPath inputFile = do + 75 (ex, sout, serr) <- readProcessWithExitCode' pandocPath args "" + 76 + 77 when (isFail ex) $ throw $ MarkdownException serr + 78 return $ BC.concat [ "<div class=\"markdown\">\n" + 79 , sout + 80 , "\n</div>" ] + 81 + 82 where + 83 isFail ExitSuccess = False + 84 isFail _ = True + 85 + 86 -- FIXME: hardcoded path + 87 args = [ "-S", "--no-wrap", "templates/"++inputFile ] + 88 + 89 + 90 pandocBS :: FilePath -> ByteString -> IO ByteString + 91 pandocBS pandocPath s = do + 92 -- using the crummy string functions for convenience here + 93 (ex, sout, serr) <- readProcessWithExitCode' pandocPath args s + 94 + 95 when (isFail ex) $ throw $ MarkdownException serr + 96 return $ BC.concat [ "<div class=\"markdown\">\n" + 97 , sout + 98 , "\n</div>" ] + 99 + 100 where + 101 isFail ExitSuccess = False + 102 isFail _ = True + 103 args = [ "-S", "--no-wrap" ] + 104 + 105 + 106 -- a version of readProcessWithExitCode that does I/O properly + 107 readProcessWithExitCode' + 108 :: FilePath -- ^ command to run + 109 -> [String] -- ^ any arguments + 110 -> ByteString -- ^ standard input + 111 -> IO (ExitCode,ByteString,ByteString) -- ^ exitcode, stdout, stderr + 112 readProcessWithExitCode' cmd args input = do + 113 (Just inh, Just outh, Just errh, pid) <- + 114 createProcess (proc cmd args){ std_in = CreatePipe, + 115 std_out = CreatePipe, + 116 std_err = CreatePipe } + 117 outMVar <- newEmptyMVar 118 - 119 -- fork off a thread to start consuming stdout - 120 forkIO $ do - 121 out <- B.hGetContents outh - 122 putMVar outM out - 123 putMVar outMVar () - 124 - 125 -- fork off a thread to start consuming stderr - 126 forkIO $ do - 127 err <- B.hGetContents errh - 128 putMVar errM err - 129 putMVar outMVar () - 130 - 131 -- now write and flush any input - 132 when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh - 133 hClose inh -- done with stdin - 134 - 135 -- wait on the output - 136 takeMVar outMVar - 137 takeMVar outMVar - 138 hClose outh - 139 - 140 -- wait on the process - 141 ex <- waitForProcess pid + 119 outM <- newEmptyMVar + 120 errM <- newEmptyMVar + 121 + 122 -- fork off a thread to start consuming stdout + 123 forkIO $ do + 124 out <- B.hGetContents outh + 125 putMVar outM out + 126 putMVar outMVar () + 127 + 128 -- fork off a thread to start consuming stderr + 129 forkIO $ do + 130 err <- B.hGetContents errh + 131 putMVar errM err + 132 putMVar outMVar () + 133 + 134 -- now write and flush any input + 135 when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh + 136 hClose inh -- done with stdin + 137 + 138 -- wait on the output + 139 takeMVar outMVar + 140 takeMVar outMVar + 141 hClose outh 142 - 143 out <- readMVar outM - 144 err <- readMVar errM + 143 -- wait on the process + 144 ex <- waitForProcess pid 145 - 146 return (ex, out, err) - 147 - 148 - 149 + 146 out <- readMVar outM + 147 err <- readMVar errM + 148 + 149 return (ex, out, err) 150 + 151 + 152 + 153 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Static.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Static.hs.html index fab1cd4..8b0d1ae 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Splices.Static.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Splices.Static.hs.html @@ -35,91 +35,92 @@ 24 25 ------------------------------------------------------------------------------ 26 import Text.Templating.Heist.Internal - 27 + 27 import Text.Templating.Heist.Types 28 - 29 ------------------------------------------------------------------------------ - 30 -- | State for storing static tag information - 31 newtype StaticTagState = STS (MVar (Map ByteString Template)) - 32 + 29 + 30 ------------------------------------------------------------------------------ + 31 -- | State for storing static tag information + 32 newtype StaticTagState = STS (MVar (Map ByteString Template)) 33 - 34 ------------------------------------------------------------------------------ - 35 -- | Clears the static tag state. - 36 clearStaticTagCache :: StaticTagState -> IO () - 37 clearStaticTagCache (STS staticMVar) = - 38 modifyMVar_ staticMVar (const $ return Map.empty) - 39 + 34 + 35 ------------------------------------------------------------------------------ + 36 -- | Clears the static tag state. + 37 clearStaticTagCache :: StaticTagState -> IO () + 38 clearStaticTagCache (STS staticMVar) = + 39 modifyMVar_ staticMVar (const $ return Map.empty) 40 - 41 ------------------------------------------------------------------------------ - 42 -- | The "static" splice ensures that its contents are evaluated once and then - 43 -- cached. The cached contents are returned every time the splice is - 44 -- referenced. - 45 staticImpl :: (MonadIO m) - 46 => StaticTagState - 47 -> TemplateMonad m Template - 48 staticImpl (STS mv) = do - 49 tree <- getParamNode - 50 let i = fromJust $ getAttribute tree "id" - 51 - 52 mp <- liftIO $ readMVar mv - 53 - 54 (mp',ns) <- do - 55 let mbn = Map.lookup i mp - 56 case mbn of - 57 Nothing -> do - 58 nodes' <- runNodeList $ getChildren tree - 59 return $! (Map.insert i nodes' mp, nodes') - 60 (Just n) -> do - 61 stopRecursion - 62 return $! (mp,n) - 63 - 64 liftIO $ modifyMVar_ mv (const $ return mp') - 65 - 66 return ns - 67 + 41 + 42 ------------------------------------------------------------------------------ + 43 -- | The "static" splice ensures that its contents are evaluated once and then + 44 -- cached. The cached contents are returned every time the splice is + 45 -- referenced. + 46 staticImpl :: (MonadIO m) + 47 => StaticTagState + 48 -> TemplateMonad m Template + 49 staticImpl (STS mv) = do + 50 tree <- getParamNode + 51 let i = fromJust $ getAttribute tree "id" + 52 + 53 mp <- liftIO $ readMVar mv + 54 + 55 (mp',ns) <- do + 56 let mbn = Map.lookup i mp + 57 case mbn of + 58 Nothing -> do + 59 nodes' <- runNodeList $ getChildren tree + 60 return $! (Map.insert i nodes' mp, nodes') + 61 (Just n) -> do + 62 stopRecursion + 63 return $! (mp,n) + 64 + 65 liftIO $ modifyMVar_ mv (const $ return mp') + 66 + 67 return ns 68 - 69 ------------------------------------------------------------------------------ - 70 -- | Modifies a TemplateState to include a "static" tag. - 71 bindStaticTag :: MonadIO m - 72 => TemplateState m - 73 -> IO (TemplateState m, StaticTagState) - 74 bindStaticTag ts = do - 75 sr <- newIORef $ Set.empty - 76 mv <- liftM STS $ newMVar Map.empty - 77 - 78 return $ (addOnLoadHook (assignIds sr) $ - 79 bindSplice "static" (staticImpl mv) ts, - 80 mv) - 81 - 82 where - 83 generateId :: IO Int - 84 generateId = getStdRandom random - 85 - 86 assignIds setref = mapM f - 87 where - 88 f node = g $ fromTree node - 89 - 90 getId = do - 91 i <- liftM (B.pack . show) generateId - 92 st <- readIORef setref - 93 if Set.member i st - 94 then getId - 95 else do - 96 writeIORef setref $ Set.insert i st - 97 return i - 98 - 99 g curs = do - 100 let node = current curs - 101 curs' <- if getName node == "static" - 102 then do - 103 i <- getId - 104 return $ modifyContent (setAttribute "id" i) curs - 105 else return curs - 106 let mbc = nextDF curs' - 107 maybe (return $ toTree curs') g mbc - 108 + 69 + 70 ------------------------------------------------------------------------------ + 71 -- | Modifies a TemplateState to include a "static" tag. + 72 bindStaticTag :: MonadIO m + 73 => TemplateState m + 74 -> IO (TemplateState m, StaticTagState) + 75 bindStaticTag ts = do + 76 sr <- newIORef $ Set.empty + 77 mv <- liftM STS $ newMVar Map.empty + 78 + 79 return $ (addOnLoadHook (assignIds sr) $ + 80 bindSplice "static" (staticImpl mv) ts, + 81 mv) + 82 + 83 where + 84 generateId :: IO Int + 85 generateId = getStdRandom random + 86 + 87 assignIds setref = mapM f + 88 where + 89 f node = g $ fromTree node + 90 + 91 getId = do + 92 i <- liftM (B.pack . show) generateId + 93 st <- readIORef setref + 94 if Set.member i st + 95 then getId + 96 else do + 97 writeIORef setref $ Set.insert i st + 98 return i + 99 + 100 g curs = do + 101 let node = current curs + 102 curs' <- if getName node == "static" + 103 then do + 104 i <- getId + 105 return $ modifyContent (setAttribute "id" i) curs + 106 else return curs + 107 let mbc = nextDF curs' + 108 maybe (return $ toTree curs') g mbc 109 110 111 + 112 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Tests.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Tests.hs.html index 74e193f..f323c8d 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.Tests.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.Tests.hs.html @@ -15,367 +15,414 @@ 4 , quickRender 5 ) where 6 - 7 import Test.Framework (Test) - 8 import Test.Framework.Providers.HUnit - 9 import Test.Framework.Providers.QuickCheck2 - 10 import qualified Test.HUnit as H - 11 import Test.QuickCheck - 12 import Test.QuickCheck.Monadic - 13 - 14 import Control.Monad.State - 15 - 16 import Data.ByteString.Char8 (ByteString) - 17 import qualified Data.ByteString.Char8 as B - 18 import qualified Data.ByteString.Lazy.Char8 as L - 19 import qualified Data.Map as Map - 20 import Data.Maybe - 21 import Data.Monoid + 7 ------------------------------------------------------------------------------ + 8 import Control.Monad.State + 9 import Data.ByteString.Char8 (ByteString) + 10 import qualified Data.ByteString.Char8 as B + 11 import qualified Data.ByteString.Lazy.Char8 as L + 12 import qualified Data.Map as Map + 13 import Data.Maybe + 14 import Data.Monoid + 15 import System.IO.Unsafe + 16 import Test.Framework (Test) + 17 import Test.Framework.Providers.HUnit + 18 import Test.Framework.Providers.QuickCheck2 + 19 import qualified Test.HUnit as H + 20 import Test.QuickCheck + 21 import Test.QuickCheck.Monadic 22 - 23 import System.IO.Unsafe - 24 + 23 + 24 ------------------------------------------------------------------------------ 25 import Text.Templating.Heist 26 import Text.Templating.Heist.Internal - 27 import Text.XML.Expat.Cursor - 28 import Text.XML.Expat.Format - 29 import qualified Text.XML.Expat.Tree as X - 30 - 31 tests :: [Test] - 32 tests = [ testProperty "simpleBindTest" $ monadicIO $ forAllM arbitrary prop_simpleBindTest - 33 , testProperty "simpleApplyTest" $ monadicIO $ forAllM arbitrary prop_simpleApplyTest - 34 , testCase "stateMonoidTest" monoidTest - 35 , testCase "templateAddTest" addTest - 36 , testCase "getDocTest" getDocTest - 37 , testCase "loadTest" loadTest - 38 , testCase "fsLoadTest" fsLoadTest - 39 , testCase "renderNoNameTest" renderNoNameTest - 40 ] - 41 - 42 monoidTest :: IO () - 43 monoidTest = do - 44 H.assertBool "left monoid identity" $ mempty `mappend` es == es - 45 H.assertBool "right monoid identity" $ es `mappend` mempty == es - 46 where es = emptyTemplateState :: TemplateState IO - 47 - 48 addTest :: IO () - 49 addTest = do - 50 H.assertBool "lookup test" $ Just [] == (fmap fst $ lookupTemplate "aoeu" ts) - 51 H.assertBool "splice touched" $ Map.size (_spliceMap ts) == 0 - 52 where ts = addTemplate "aoeu" [] (mempty::TemplateState IO) - 53 - 54 isLeft :: Either a b -> Bool - 55 isLeft (Left _) = True - 56 isLeft (Right _) = False - 57 - 58 - 59 loadT :: String -> IO (Either String (TemplateState IO)) - 60 loadT s = loadTemplates s emptyTemplateState - 61 - 62 loadTest :: H.Assertion - 63 loadTest = do - 64 ets <- loadT "templates" - 65 either (error "Error loading templates") - 66 (\ts -> do let tm = _templateMap ts - 67 H.assertBool "loadTest size" $ Map.size tm == 12 - 68 ) ets + 27 import Text.Templating.Heist.Types + 28 import Text.Templating.Heist.Splices.Apply + 29 import Text.XML.Expat.Cursor + 30 import Text.XML.Expat.Format + 31 import qualified Text.XML.Expat.Tree as X + 32 + 33 tests :: [Test] + 34 tests = [ testProperty "simpleBindTest" $ monadicIO $ forAllM arbitrary prop_simpleBindTest + 35 , testProperty "simpleApplyTest" $ monadicIO $ forAllM arbitrary prop_simpleApplyTest + 36 , testCase "stateMonoidTest" monoidTest + 37 , testCase "templateAddTest" addTest + 38 , testCase "getDocTest" getDocTest + 39 , testCase "loadTest" loadTest + 40 , testCase "fsLoadTest" fsLoadTest + 41 , testCase "renderNoNameTest" renderNoNameTest + 42 , testCase "doctypeTest" doctypeTest + 43 , testCase "attributeSubstitution" attrSubst + 44 , testCase "applyTest" applyTest + 45 ] + 46 + 47 applyTest :: H.Assertion + 48 applyTest = do + 49 let es = emptyTemplateState :: TemplateState IO + 50 res <- evalTemplateMonad applyImpl + 51 (X.Element "apply" [("template", "nonexistant")] []) es + 52 H.assertEqual "apply nothing" res [] + 53 + 54 monoidTest :: IO () + 55 monoidTest = do + 56 H.assertBool "left monoid identity" $ mempty `mappend` es == es + 57 H.assertBool "right monoid identity" $ es `mappend` mempty == es + 58 where es = emptyTemplateState :: TemplateState IO + 59 + 60 addTest :: IO () + 61 addTest = do + 62 H.assertBool "lookup test" $ Just [] == (fmap (_itNodes . fst) $ lookupTemplate "aoeu" ts) + 63 H.assertBool "splice touched" $ Map.size (_spliceMap ts) == 0 + 64 where ts = addTemplate "aoeu" (InternalTemplate Nothing []) (mempty::TemplateState IO) + 65 + 66 isLeft :: Either a b -> Bool + 67 isLeft (Left _) = True + 68 isLeft (Right _) = False 69 - 70 renderNoNameTest :: H.Assertion - 71 renderNoNameTest = do - 72 ets <- loadT "templates" - 73 either (error "Error loading templates") - 74 (\ts -> do t <- renderTemplate ts "" - 75 H.assertBool "renderNoName" $ t == Nothing - 76 ) ets - 77 - 78 getDocTest :: H.Assertion - 79 getDocTest = do - 80 d <- getDoc "bkteoar" - 81 H.assertBool "non-existent doc" $ isLeft d - 82 f <- getDoc "templates/index.tpl" - 83 H.assertBool "index doc" $ not $ isLeft f - 84 - 85 fsLoadTest :: H.Assertion - 86 fsLoadTest = do - 87 ets <- loadT "templates" - 88 let tm = either (error "Error loading templates") _templateMap ets - 89 let ts = setTemplates tm emptyTemplateState :: TemplateState IO - 90 f p n = H.assertBool ("loading template "++n) $ p $ lookupTemplate (B.pack n) ts - 91 f isNothing "abc/def/xyz" - 92 f isJust "a" - 93 f isJust "bar/a" - 94 f isJust "/bar/a" - 95 - 96 -- dotdotTest :: H.Assertion - 97 -- dotdotTest = do - 98 -- ets <- loadT "templates" - 99 -- let tm = either (error "Error loading templates") _templateMap ets - 100 -- let ts = setTemplates tm emptyTemplateState :: TemplateState IO - 101 -- f p n = H.assertBool ("loading template "++n) $ p $ lookupTemplate (B.pack n) ts - 102 - 103 identStartChar :: [Char] - 104 identStartChar = ['a'..'z'] - 105 identChar :: [Char] - 106 identChar = '_' : identStartChar + 70 + 71 loadT :: String -> IO (Either String (TemplateState IO)) + 72 loadT s = loadTemplates s emptyTemplateState + 73 + 74 loadTest :: H.Assertion + 75 loadTest = do + 76 ets <- loadT "templates" + 77 either (error "Error loading templates") + 78 (\ts -> do let tm = _templateMap ts + 79 H.assertBool "loadTest size" $ Map.size tm == 15 + 80 ) ets + 81 + 82 renderNoNameTest :: H.Assertion + 83 renderNoNameTest = do + 84 ets <- loadT "templates" + 85 either (error "Error loading templates") + 86 (\ts -> do t <- renderTemplate ts "" + 87 H.assertBool "renderNoName" $ t == Nothing + 88 ) ets + 89 + 90 getDocTest :: H.Assertion + 91 getDocTest = do + 92 d <- getDoc "bkteoar" + 93 H.assertBool "non-existent doc" $ isLeft d + 94 f <- getDoc "templates/index.tpl" + 95 H.assertBool "index doc" $ not $ isLeft f + 96 + 97 fsLoadTest :: H.Assertion + 98 fsLoadTest = do + 99 ets <- loadT "templates" + 100 let tm = either (error "Error loading templates") _templateMap ets + 101 let ts = setTemplates tm emptyTemplateState :: TemplateState IO + 102 f p n = H.assertBool ("loading template "++n) $ p $ lookupTemplate (B.pack n) ts + 103 f isNothing "abc/def/xyz" + 104 f isJust "a" + 105 f isJust "bar/a" + 106 f isJust "/bar/a" 107 - 108 newtype Name = Name { unName :: B.ByteString } deriving (Show) - 109 - 110 instance Arbitrary Name where - 111 arbitrary = do - 112 first <- elements identStartChar - 113 n <- choose (4,10) - 114 rest <- vectorOf n $ elements identChar - 115 return $ Name $ B.pack $ first : rest + 108 doctypeTest :: H.Assertion + 109 doctypeTest = do + 110 ets <- loadT "templates" + 111 let ts = either (error "Error loading templates") id ets + 112 index <- renderTemplate ts "index" + 113 H.assertBool "doctype test index" $ hasDoctype $ fromJust index + 114 ioc <- renderTemplate ts "ioc" + 115 H.assertBool "doctype test ioc" $ hasDoctype $ fromJust ioc 116 - 117 instance Arbitrary Node where - 118 arbitrary = limitedDepth 3 - 119 shrink (X.Text _) = [] - 120 shrink (X.Element _ [] []) = [] - 121 shrink (X.Element n [] (_:cs)) = [X.Element n [] cs] - 122 shrink (X.Element n (_:as) []) = [X.Element n as []] - 123 shrink (X.Element n as cs) = [X.Element n as (tail cs), X.Element n (tail as) cs] - 124 - 125 textGen :: Gen [Char] - 126 textGen = listOf $ elements ((replicate 5 ' ') ++ identStartChar) - 127 - 128 limitedDepth :: Int -> Gen Node - 129 limitedDepth 0 = liftM (X.Text . B.pack) textGen - 130 limitedDepth n = oneof [ liftM (X.Text . B.pack) textGen - 131 , liftM3 X.Element arbitrary - 132 (liftM (take 2) arbitrary) - 133 (liftM (take 3) $ listOf $ limitedDepth (n-1)) - 134 ] - 135 - 136 instance Arbitrary B.ByteString where - 137 arbitrary = liftM unName arbitrary + 117 attrSubst :: H.Assertion + 118 attrSubst = do + 119 ets <- loadT "templates" + 120 let ts = either (error "Error loading templates") id ets + 121 check (setTs "meaning_of_everything" ts) "pre_meaning_of_everything_post" + 122 check ts "pre__post" + 123 where + 124 setTs val = bindSplice "foo" (return [X.Text val]) + 125 check ts str = do + 126 res <- renderTemplate ts "attrs" + 127 H.assertBool ("attr subst "++(show str)) $ + 128 not $ B.null $ snd $ B.breakSubstring str $ fromJust res + 129 H.assertBool ("attr subst foo") $ + 130 not $ B.null $ snd $ B.breakSubstring "$(foo)" $ fromJust res + 131 + 132 -- dotdotTest :: H.Assertion + 133 -- dotdotTest = do + 134 -- ets <- loadT "templates" + 135 -- let tm = either (error "Error loading templates") _templateMap ets + 136 -- let ts = setTemplates tm emptyTemplateState :: TemplateState IO + 137 -- f p n = H.assertBool ("loading template "++n) $ p $ lookupTemplate (B.pack n) ts 138 - 139 {- - 140 - Code for inserting nodes into any point of a tree - 141 -} - 142 - 143 type Loc = Cursor B.ByteString B.ByteString - 144 type Insert a = State Int a + 139 identStartChar :: [Char] + 140 identStartChar = ['a'..'z'] + 141 identChar :: [Char] + 142 identChar = '_' : identStartChar + 143 + 144 newtype Name = Name { unName :: B.ByteString } deriving (Show) 145 - 146 {- - 147 - Returns the number of unique insertion points in the tree. - 148 - If h = insertAt f n g", the following property holds: - 149 - insSize h == (insSize f) + (insSize g) - 1 - 150 -} - 151 insSize :: [X.Node tag text] -> Int - 152 insSize ns = 1 + (sum $ map nodeSize ns) - 153 where nodeSize (X.Text _) = 1 - 154 nodeSize (X.Element _ _ c) = 1 + (insSize c) - 155 - 156 insertAt :: [Node] -> Int -> [Node] -> [Node] - 157 insertAt elems 0 ns = elems ++ ns - 158 insertAt elems _ [] = elems - 159 insertAt elems n list = maybe [] (toForest . root) $ - 160 evalState (processNode elems $ fromJust $ fromForest list) n - 161 - 162 move :: Insert () - 163 move = modify (\x -> x-1) - 164 - 165 processNode :: [Node] -> Loc -> Insert (Maybe Loc) - 166 processNode elems loc = liftM2 mplus (move >> goDown loc) (move >> goRight loc) - 167 where goDown l = case current l of - 168 X.Text _ -> modify (+1) >> return Nothing - 169 X.Element _ _ _ -> doneCheck (insertManyFirstChild elems) firstChild l - 170 goRight = doneCheck (Just . insertManyRight elems) right - 171 doneCheck insertFunc next l = do - 172 s <- get - 173 if s == 0 - 174 then return $ insertFunc l - 175 else maybe (return Nothing) (processNode elems) $ next l - 176 - 177 {- - 178 - <bind> tests - 179 -} - 180 - 181 -- Data type encapsulating the parameters for a bind operation - 182 data Bind = Bind { - 183 _bindElemName :: Name, - 184 _bindChildren :: [Node], - 185 _bindDoc :: [Node], - 186 _bindPos :: Int, - 187 _bindRefPos :: Int - 188 } -- deriving (Show) - 189 - 190 instance Show Bind where - 191 show b@(Bind e c d p r) = unlines - 192 ["\n" - 193 ,"Bind element name: "++(show e) - 194 ,"Bind pos: "++(show p) - 195 ,"Bind ref pos: "++(show r) - 196 ,"Bind document:" - 197 ,L.unpack $ L.concat $ map formatNode d - 198 ,"Bind children:" - 199 ,L.unpack $ L.concat $ map formatNode c - 200 ,"Result:" - 201 ,L.unpack $ L.concat $ map formatNode $ buildResult b - 202 ,"Splice result:" - 203 ,L.unpack $ L.concat $ map formatNode $ unsafePerformIO $ - 204 runRawTemplate emptyTemplateState $ buildBindTemplate b - 205 ,"Template:" - 206 ,L.unpack $ L.concat $ map formatNode $ buildBindTemplate b - 207 ] - 208 - 209 buildNode :: B.ByteString -> B.ByteString -> Bind -> Node - 210 buildNode tag attr (Bind s c _ _ _) = X.Element tag [(attr, unName s)] c - 211 - 212 buildBind :: Bind -> Node - 213 buildBind = buildNode "bind" "tag" - 214 - 215 instance Arbitrary Bind where - 216 arbitrary = do - 217 name <- arbitrary - 218 kids <- liftM (take 3) arbitrary - 219 doc <- liftM (take 5) arbitrary - 220 let s = insSize doc - 221 loc <- choose (0, s-1) - 222 loc2 <- choose (0, s-loc-1) - 223 return $ Bind name kids doc loc loc2 - 224 shrink (Bind e [c] (_:ds) p r) = [Bind e [c] ds p r] - 225 shrink (Bind e (_:cs) d p r) = [Bind e cs d p r] - 226 shrink _ = [] - 227 - 228 empty :: tag -> X.Node tag text - 229 empty n = X.Element n [] [] - 230 - 231 buildBindTemplate :: Bind -> [Node] - 232 buildBindTemplate s@(Bind n _ d b r) = - 233 insertAt [empty $ unName $ n] pos $ withBind - 234 where bind = [buildBind s] - 235 bindSize = insSize bind - 236 withBind = insertAt bind b d - 237 pos = b + bindSize - 1 + r - 238 - 239 buildResult :: Bind -> [Node] - 240 buildResult (Bind _ c d b r) = insertAt c (b+r) d - 241 - 242 prop_simpleBindTest :: Bind -> PropertyM IO () - 243 prop_simpleBindTest bind = do - 244 let template = buildBindTemplate bind - 245 result = buildResult bind - 246 spliceResult <- run $ runRawTemplate emptyTemplateState template - 247 assert $ result == spliceResult + 146 instance Arbitrary Name where + 147 arbitrary = do + 148 first <- elements identStartChar + 149 n <- choose (4,10) + 150 rest <- vectorOf n $ elements identChar + 151 return $ Name $ B.pack $ first : rest + 152 + 153 instance Arbitrary Node where + 154 arbitrary = limitedDepth 3 + 155 shrink (X.Text _) = [] + 156 shrink (X.Element _ [] []) = [] + 157 shrink (X.Element n [] (_:cs)) = [X.Element n [] cs] + 158 shrink (X.Element n (_:as) []) = [X.Element n as []] + 159 shrink (X.Element n as cs) = [X.Element n as (tail cs), X.Element n (tail as) cs] + 160 + 161 textGen :: Gen [Char] + 162 textGen = listOf $ elements ((replicate 5 ' ') ++ identStartChar) + 163 + 164 limitedDepth :: Int -> Gen Node + 165 limitedDepth 0 = liftM (X.Text . B.pack) textGen + 166 limitedDepth n = oneof [ liftM (X.Text . B.pack) textGen + 167 , liftM3 X.Element arbitrary + 168 (liftM (take 2) arbitrary) + 169 (liftM (take 3) $ listOf $ limitedDepth (n-1)) + 170 ] + 171 + 172 instance Arbitrary B.ByteString where + 173 arbitrary = liftM unName arbitrary + 174 + 175 {- + 176 - Code for inserting nodes into any point of a tree + 177 -} + 178 + 179 type Loc = Cursor B.ByteString B.ByteString + 180 type Insert a = State Int a + 181 + 182 {- + 183 - Returns the number of unique insertion points in the tree. + 184 - If h = insertAt f n g", the following property holds: + 185 - insSize h == (insSize f) + (insSize g) - 1 + 186 -} + 187 insSize :: [X.Node tag text] -> Int + 188 insSize ns = 1 + (sum $ map nodeSize ns) + 189 where nodeSize (X.Text _) = 1 + 190 nodeSize (X.Element _ _ c) = 1 + (insSize c) + 191 + 192 insertAt :: [Node] -> Int -> [Node] -> [Node] + 193 insertAt elems 0 ns = elems ++ ns + 194 insertAt elems _ [] = elems + 195 insertAt elems n list = maybe [] (toForest . root) $ + 196 evalState (processNode elems $ fromJust $ fromForest list) n + 197 + 198 move :: Insert () + 199 move = modify (\x -> x-1) + 200 + 201 processNode :: [Node] -> Loc -> Insert (Maybe Loc) + 202 processNode elems loc = liftM2 mplus (move >> goDown loc) (move >> goRight loc) + 203 where goDown l = case current l of + 204 X.Text _ -> modify (+1) >> return Nothing + 205 X.Element _ _ _ -> doneCheck (insertManyFirstChild elems) firstChild l + 206 goRight = doneCheck (Just . insertManyRight elems) right + 207 doneCheck insertFunc next l = do + 208 s <- get + 209 if s == 0 + 210 then return $ insertFunc l + 211 else maybe (return Nothing) (processNode elems) $ next l + 212 + 213 {- + 214 - <bind> tests + 215 -} + 216 + 217 -- Data type encapsulating the parameters for a bind operation + 218 data Bind = Bind { + 219 _bindElemName :: Name, + 220 _bindChildren :: [Node], + 221 _bindDoc :: [Node], + 222 _bindPos :: Int, + 223 _bindRefPos :: Int + 224 } -- deriving (Show) + 225 + 226 instance Show Bind where + 227 show b@(Bind e c d p r) = unlines + 228 ["\n" + 229 ,"Bind element name: "++(show e) + 230 ,"Bind pos: "++(show p) + 231 ,"Bind ref pos: "++(show r) + 232 ,"Bind document:" + 233 ,L.unpack $ L.concat $ map formatNode d + 234 ,"Bind children:" + 235 ,L.unpack $ L.concat $ map formatNode c + 236 ,"Result:" + 237 ,L.unpack $ L.concat $ map formatNode $ buildResult b + 238 ,"Splice result:" + 239 ,L.unpack $ L.concat $ map formatNode $ unsafePerformIO $ + 240 evalTemplateMonad (runNodeList $ buildBindTemplate b) + 241 (X.Text "") emptyTemplateState + 242 ,"Template:" + 243 ,L.unpack $ L.concat $ map formatNode $ buildBindTemplate b + 244 ] + 245 + 246 buildNode :: B.ByteString -> B.ByteString -> Bind -> Node + 247 buildNode tag attr (Bind s c _ _ _) = X.Element tag [(attr, unName s)] c 248 - 249 {- - 250 - <apply> tests - 251 -} - 252 - 253 data Apply = Apply { - 254 _applyName :: Name, - 255 _applyCaller :: [Node], - 256 _applyCallee :: Template, - 257 _applyChildren :: [Node], - 258 _applyPos :: Int - 259 } deriving (Show) - 260 - 261 instance Arbitrary Apply where - 262 arbitrary = do - 263 name <- arbitrary - 264 kids <- liftM (take 3) $ listOf $ limitedDepth 2 - 265 caller <- liftM (take 5) arbitrary - 266 callee <- liftM (take 1) $ listOf $ limitedDepth 3 - 267 let s = insSize caller - 268 loc <- choose (0, s-1) - 269 return $ Apply name caller callee kids loc - 270 - 271 buildApplyCaller :: Apply -> [Node] - 272 buildApplyCaller (Apply name caller _ kids pos) = - 273 insertAt [X.Element "apply" [("template", unName name)] kids] pos caller - 274 - 275 calcCorrect :: Apply -> [Node] - 276 calcCorrect (Apply _ caller callee _ pos) = insertAt callee pos caller - 277 - 278 calcResult :: (MonadIO m) => Apply -> m [Node] - 279 calcResult apply@(Apply name _ callee _ _) = - 280 runRawTemplate ts $ buildApplyCaller apply - 281 where ts = setTemplates (Map.singleton [unName name] callee) emptyTemplateState - 282 - 283 prop_simpleApplyTest :: Apply -> PropertyM IO () - 284 prop_simpleApplyTest apply = do - 285 let correct = calcCorrect apply - 286 result <- run $ calcResult apply - 287 assert $ correct == result + 249 buildBind :: Bind -> Node + 250 buildBind = buildNode "bind" "tag" + 251 + 252 instance Arbitrary Bind where + 253 arbitrary = do + 254 name <- arbitrary + 255 kids <- liftM (take 3) arbitrary + 256 doc <- liftM (take 5) arbitrary + 257 let s = insSize doc + 258 loc <- choose (0, s-1) + 259 loc2 <- choose (0, s-loc-1) + 260 return $ Bind name kids doc loc loc2 + 261 shrink (Bind e [c] (_:ds) p r) = [Bind e [c] ds p r] + 262 shrink (Bind e (_:cs) d p r) = [Bind e cs d p r] + 263 shrink _ = [] + 264 + 265 empty :: tag -> X.Node tag text + 266 empty n = X.Element n [] [] + 267 + 268 buildBindTemplate :: Bind -> [Node] + 269 buildBindTemplate s@(Bind n _ d b r) = + 270 insertAt [empty $ unName $ n] pos $ withBind + 271 where bind = [buildBind s] + 272 bindSize = insSize bind + 273 withBind = insertAt bind b d + 274 pos = b + bindSize - 1 + r + 275 + 276 buildResult :: Bind -> [Node] + 277 buildResult (Bind _ c d b r) = insertAt c (b+r) d + 278 + 279 prop_simpleBindTest :: Bind -> PropertyM IO () + 280 prop_simpleBindTest bind = do + 281 let template = buildBindTemplate bind + 282 result = buildResult bind + 283 spliceResult <- run $ evalTemplateMonad (runNodeList template) + 284 (X.Text "") + 285 emptyTemplateState + 286 + 287 assert $ result == spliceResult 288 - 289 - 290 ------------------------------------------------------------------------------ - 291 -- | Reloads the templates from disk and renders the specified - 292 -- template. (Old convenience code.) - 293 quickRender :: FilePath -> ByteString -> IO (Maybe ByteString) - 294 quickRender baseDir name = do - 295 etm <- loadTemplates baseDir emptyTemplateState - 296 let ts = either (const emptyTemplateState) id etm - 297 ns <- runTemplate ts name - 298 return $ (Just . formatList') =<< ns - 299 + 289 {- + 290 - <apply> tests + 291 -} + 292 + 293 data Apply = Apply { + 294 _applyName :: Name, + 295 _applyCaller :: [Node], + 296 _applyCallee :: Template, + 297 _applyChildren :: [Node], + 298 _applyPos :: Int + 299 } deriving (Show) 300 - 301 {- - 302 -- The beginning of some future tests for hook functions. - 303 - 304 p :: ByteString -> Node - 305 p t = X.Element "p" [] [X.Text t] - 306 - 307 hookG :: Monad m => ByteString -> Template -> m Template - 308 hookG str t = return $ (p str) : t - 309 - 310 onLoad = hookG "Inserted on load" - 311 preRun = hookG "Inserted on preRun" - 312 postRun = hookG "Inserted on postRun" - 313 - 314 ts :: IO (Either String (TemplateState IO)) - 315 ts = loadTemplates "test/templates" $ - 316 foldr ($) emptyTemplateState - 317 [setOnLoadHook onLoad - 318 ,setPreRunHook preRun - 319 ,setPostRunHook postRun - 320 ] - 321 - 322 r name etm = do - 323 let ts = either (error "Danger Will Robinson!") id etm - 324 ns <- runTemplate ts name - 325 return $ (Just . formatList') =<< ns + 301 instance Arbitrary Apply where + 302 arbitrary = do + 303 name <- arbitrary + 304 kids <- liftM (take 3) $ listOf $ limitedDepth 2 + 305 caller <- liftM (take 5) arbitrary + 306 callee <- liftM (take 1) $ listOf $ limitedDepth 3 + 307 let s = insSize caller + 308 loc <- choose (0, s-1) + 309 return $ Apply name caller callee kids loc + 310 + 311 buildApplyCaller :: Apply -> [Node] + 312 buildApplyCaller (Apply name caller _ kids pos) = + 313 insertAt [X.Element "apply" [("template", unName name)] kids] pos caller + 314 + 315 calcCorrect :: Apply -> [Node] + 316 calcCorrect (Apply _ caller callee _ pos) = insertAt callee pos caller + 317 + 318 calcResult :: (MonadIO m) => Apply -> m [Node] + 319 calcResult apply@(Apply name _ callee _ _) = + 320 evalTemplateMonad (runNodeList $ buildApplyCaller apply) + 321 (X.Text "") ts + 322 + 323 where ts = setTemplates (Map.singleton [unName name] + 324 (InternalTemplate Nothing callee)) + 325 emptyTemplateState 326 - 327 - 328 -} - 329 - 330 - 331 {- - 332 - Convenience code for manual ghci experimentation - 333 -} - 334 - 335 --html :: [Node] -> Node - 336 --html c = X.Element "html" [] [hhead, body c] - 337 --hhead :: Node - 338 --hhead = X.Element "head" [] [title, X.Element "script" [] []] - 339 --title :: Node - 340 --title = X.Element "title" [] [X.Text "Test Page"] - 341 --body :: [Node] -> Node - 342 --body = X.Element "body" [] - 343 -- - 344 --para :: Int -> Node - 345 --para n = X.Element "p" [] [X.Text $ B.pack $ "This is paragraph " ++ show n] - 346 --para2 :: B.ByteString -> Node - 347 --para2 c = X.Element "p" [] [X.Text c] - 348 --para3 :: Node - 349 --para3 = X.Element "p" [] [X.Text "AHA!"] - 350 -- - 351 --foo :: Int -> [Node] - 352 --foo n = insertAt [X.Element "NEW" [] []] n [html [para 1, para 2]] - 353 -- - 354 --tdoc :: [Node] - 355 --tdoc = [para 1, para 2, para 3, para 4] - 356 -- - 357 --bindElem :: [Node] -> Int -> Int -> Bind - 358 --bindElem = Bind (Name "mytag") [para2 "bound paragraph"] - 359 -- - 360 --addBind :: Bind -> [Node] -> [Node] - 361 --addBind b = insertAt [buildBind b] 0 . insertAt [empty $ unName $ _bindElemName b] 2 - 362 -- - 363 --prn :: Node -> IO () - 364 --prn = L.putStrLn . formatNode - 365 --runTests :: IO () - 366 --runTests = defaultMain tests - 367 + 327 prop_simpleApplyTest :: Apply -> PropertyM IO () + 328 prop_simpleApplyTest apply = do + 329 let correct = calcCorrect apply + 330 result <- run $ calcResult apply + 331 assert $ correct == result + 332 + 333 + 334 loadTS :: FilePath -> IO (TemplateState IO) + 335 loadTS baseDir = do + 336 etm <- loadTemplates baseDir emptyTemplateState + 337 return $ either error id etm + 338 + 339 ------------------------------------------------------------------------------ + 340 -- | Reloads the templates from disk and renders the specified + 341 -- template. (Old convenience code.) + 342 quickRender :: FilePath -> ByteString -> IO (Maybe ByteString) + 343 quickRender baseDir name = do + 344 ts <- loadTS baseDir + 345 renderTemplate ts name + 346 + 347 + 348 {- + 349 -- The beginning of some future tests for hook functions. + 350 + 351 p :: ByteString -> Node + 352 p t = X.Element "p" [] [X.Text t] + 353 + 354 hookG :: Monad m => ByteString -> Template -> m Template + 355 hookG str t = return $ (p str) : t + 356 + 357 onLoad = hookG "Inserted on load" + 358 preRun = hookG "Inserted on preRun" + 359 postRun = hookG "Inserted on postRun" + 360 + 361 ts :: IO (Either String (TemplateState IO)) + 362 ts = loadTemplates "test/templates" $ + 363 foldr ($) emptyTemplateState + 364 [setOnLoadHook onLoad + 365 ,setPreRunHook preRun + 366 ,setPostRunHook postRun + 367 ] + 368 + 369 r name etm = do + 370 let ts = either (error "Danger Will Robinson!") id etm + 371 ns <- runNodeList ts name + 372 return $ (Just . formatList') =<< ns + 373 + 374 + 375 -} + 376 + 377 + 378 {- + 379 - Convenience code for manual ghci experimentation + 380 -} + 381 + 382 --html :: [Node] -> Node + 383 --html c = X.Element "html" [] [hhead, body c] + 384 --hhead :: Node + 385 --hhead = X.Element "head" [] [title, X.Element "script" [] []] + 386 --title :: Node + 387 --title = X.Element "title" [] [X.Text "Test Page"] + 388 --body :: [Node] -> Node + 389 --body = X.Element "body" [] + 390 -- + 391 --para :: Int -> Node + 392 --para n = X.Element "p" [] [X.Text $ B.pack $ "This is paragraph " ++ show n] + 393 --para2 :: B.ByteString -> Node + 394 --para2 c = X.Element "p" [] [X.Text c] + 395 --para3 :: Node + 396 --para3 = X.Element "p" [] [X.Text "AHA!"] + 397 -- + 398 --foo :: Int -> [Node] + 399 --foo n = insertAt [X.Element "NEW" [] []] n [html [para 1, para 2]] + 400 -- + 401 --tdoc :: [Node] + 402 --tdoc = [para 1, para 2, para 3, para 4] + 403 -- + 404 --bindElem :: [Node] -> Int -> Int -> Bind + 405 --bindElem = Bind (Name "mytag") [para2 "bound paragraph"] + 406 -- + 407 --addBind :: Bind -> [Node] -> [Node] + 408 --addBind b = insertAt [buildBind b] 0 . insertAt [empty $ unName $ _bindElemName b] 2 + 409 -- + 410 --prn :: Node -> IO () + 411 --prn = L.putStrLn . formatNode + 412 --runTests :: IO () + 413 --runTests = defaultMain tests + 414 diff --git a/static/docs/heist-hpc/Text.Templating.Heist.Types.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.Types.hs.html new file mode 100644 index 0000000..eea2348 --- /dev/null +++ b/static/docs/heist-hpc/Text.Templating.Heist.Types.hs.html @@ -0,0 +1,360 @@ + +
+ 1 {-# LANGUAGE OverloadedStrings #-} + 2 {-# LANGUAGE ScopedTypeVariables #-} + 3 {-# LANGUAGE GeneralizedNewtypeDeriving #-} + 4 {-# LANGUAGE FlexibleInstances #-} + 5 {-# LANGUAGE UndecidableInstances #-} + 6 {-# LANGUAGE MultiParamTypeClasses #-} + 7 + 8 {-| + 9 + 10 This module contains the core Heist data types. TemplateMonad intentionally + 11 does not expose any of it's functionality via MonadState or MonadReader + 12 functions. We define passthrough instances for the most common types of + 13 monads. These instances allow the user to use TemplateMonad in a monad stack + 14 without needing calls to `lift`. + 15 + 16 Edward Kmett wrote most of the TemplateMonad code and associated instances, + 17 liberating us from the unused writer portion of RWST. + 18 + 19 -} + 20 + 21 module Text.Templating.Heist.Types where + 22 + 23 ------------------------------------------------------------------------------ + 24 import Control.Applicative + 25 import Control.Monad.Cont + 26 import Control.Monad.Error + 27 import Control.Monad.Reader + 28 import Control.Monad.State + 29 import Control.Monad.Trans + 30 import Data.ByteString.Char8 (ByteString) + 31 import qualified Data.ByteString.Char8 as B + 32 import qualified Data.Map as Map + 33 import Data.Map (Map) + 34 import Data.Monoid + 35 import Data.Typeable + 36 import Prelude hiding (catch) + 37 import qualified Text.XML.Expat.Tree as X + 38 + 39 + 40 ------------------------------------------------------------------------------ + 41 -- | Heist templates are XML documents. The hexpat library is polymorphic over + 42 -- the type of strings, so here we define a 'Node' alias to fix the string + 43 -- types of the tag names and tag bodies to 'ByteString'. + 44 type Node = X.Node ByteString ByteString + 45 + 46 + 47 ------------------------------------------------------------------------------ + 48 -- | A 'Template' is a forest of XML nodes. Here we deviate from the "single + 49 -- root node" constraint of well-formed XML because we want to allow templates + 50 -- to contain fragments of a document that may not have a single root. + 51 type Template = [Node] + 52 + 53 + 54 ------------------------------------------------------------------------------ + 55 -- | An 'InternalTemplate' carries a doctype with it that we get from the + 56 -- template at load time. The tricks that we're playing so templates don't + 57 -- have to have a single root node screw up doctypes, so we have to handle + 58 -- them manually. + 59 data InternalTemplate = InternalTemplate { + 60 _itDoctype :: Maybe ByteString, + 61 _itNodes :: [Node] + 62 } deriving (Eq, Show) + 63 + 64 + 65 ------------------------------------------------------------------------------ + 66 -- | Reversed list of directories. This holds the path to the template + 67 -- currently being processed. + 68 type TPath = [ByteString] + 69 + 70 + 71 ------------------------------------------------------------------------------ + 72 -- | All templates are stored in a map. + 73 type TemplateMap = Map TPath InternalTemplate + 74 + 75 + 76 ------------------------------------------------------------------------------ + 77 -- | A Splice is a TemplateMonad computation that returns a 'Template'. + 78 type Splice m = TemplateMonad m Template + 79 + 80 + 81 ------------------------------------------------------------------------------ + 82 -- | SpliceMap associates a name and a Splice. + 83 type SpliceMap m = Map ByteString (Splice m) + 84 + 85 + 86 ------------------------------------------------------------------------------ + 87 -- | Holds all the state information needed for template processing. You will + 88 -- build a @TemplateState@ using any of Heist's @TemplateState m -> + 89 -- TemplateState m@ \"filter\" functions. Then you use the resulting + 90 -- @TemplateState@ in calls to @renderTemplate@. + 91 data TemplateState m = TemplateState { + 92 -- | A mapping of splice names to splice actions + 93 _spliceMap :: SpliceMap m + 94 -- | A mapping of template names to templates + 95 , _templateMap :: TemplateMap + 96 -- | A flag to control splice recursion + 97 , _recurse :: Bool + 98 -- | The path to the template currently being processed. + 99 , _curContext :: TPath + 100 -- | A counter keeping track of the current recursion depth to prevent + 101 -- infinite loops. + 102 , _recursionDepth :: Int + 103 -- | A hook run on all templates at load time. + 104 , _onLoadHook :: Template -> IO Template + 105 -- | A hook run on all templates just before they are rendered. + 106 , _preRunHook :: Template -> m Template + 107 -- | A hook run on all templates just after they are rendered. + 108 , _postRunHook :: Template -> m Template + 109 -- | The doctypes encountered during template processing. + 110 , _doctypes :: [ByteString] + 111 } + 112 + 113 + 114 ------------------------------------------------------------------------------ + 115 instance (Monad m) => Monoid (TemplateState m) where + 116 mempty = TemplateState Map.empty Map.empty True [] 0 + 117 return return return [] + 118 + 119 (TemplateState s1 t1 r1 _ d1 o1 b1 a1 dt1) `mappend` + 120 (TemplateState s2 t2 r2 c2 d2 o2 b2 a2 dt2) = + 121 TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2) + 122 (dt1 `mappend` dt2) + 123 where + 124 s = s1 `mappend` s2 + 125 t = t1 `mappend` t2 + 126 r = r1 && r2 + 127 d = max d1 d2 + 128 + 129 + 130 ------------------------------------------------------------------------------ + 131 instance Eq (TemplateState m) where + 132 a == b = (_recurse a == _recurse b) && + 133 (_templateMap a == _templateMap b) && + 134 (_curContext a == _curContext b) + 135 + 136 + 137 ------------------------------------------------------------------------------ + 138 -- | TemplateMonad is the monad used for 'Splice' processing. TemplateMonad + 139 -- provides \"passthrough\" instances for many of the monads you might use in + 140 -- the inner monad. + 141 newtype TemplateMonad m a = TemplateMonad { + 142 runTemplateMonad :: Node + 143 -> TemplateState m + 144 -> m (a, TemplateState m) + 145 } + 146 + 147 + 148 ------------------------------------------------------------------------------ + 149 -- | Evaluates a template monad as a computation in the underlying monad. + 150 evalTemplateMonad :: Monad m + 151 => TemplateMonad m a + 152 -> Node + 153 -> TemplateState m + 154 -> m a + 155 evalTemplateMonad m r s = do + 156 (a, _) <- runTemplateMonad m r s + 157 return a + 158 + 159 ------------------------------------------------------------------------------ + 160 -- | Helper function for the functor instance + 161 first :: (a -> b) -> (a, c) -> (b, c) + 162 first f (a,b) = (f a, b) + 163 + 164 + 165 ------------------------------------------------------------------------------ + 166 -- | Functor instance + 167 instance Functor m => Functor (TemplateMonad m) where + 168 fmap f (TemplateMonad m) = TemplateMonad $ \r s -> first f <$> m r s + 169 + 170 + 171 ------------------------------------------------------------------------------ + 172 -- | Applicative instance + 173 instance (Monad m, Functor m) => Applicative (TemplateMonad m) where + 174 pure = return + 175 (<*>) = ap + 176 + 177 + 178 ------------------------------------------------------------------------------ + 179 -- | Monad instance + 180 instance Monad m => Monad (TemplateMonad m) where + 181 return a = TemplateMonad (\_ s -> return (a, s)) + 182 TemplateMonad m >>= k = TemplateMonad $ \r s -> do + 183 (a, s') <- m r s + 184 runTemplateMonad (k a) r s' + 185 + 186 + 187 ------------------------------------------------------------------------------ + 188 -- | MonadIO instance + 189 instance MonadIO m => MonadIO (TemplateMonad m) where + 190 liftIO = lift . liftIO + 191 + 192 + 193 ------------------------------------------------------------------------------ + 194 -- | MonadTrans instance + 195 instance MonadTrans TemplateMonad where + 196 lift m = TemplateMonad $ \_ s -> do + 197 a <- m + 198 return (a, s) + 199 + 200 + 201 ------------------------------------------------------------------------------ + 202 -- | MonadFix passthrough instance + 203 instance MonadFix m => MonadFix (TemplateMonad m) where + 204 mfix f = TemplateMonad $ \r s -> + 205 mfix $ \ (a, _) -> runTemplateMonad (f a) r s + 206 + 207 + 208 ------------------------------------------------------------------------------ + 209 -- | Alternative passthrough instance + 210 instance (Functor m, MonadPlus m) => Alternative (TemplateMonad m) where + 211 empty = mzero + 212 (<|>) = mplus + 213 + 214 + 215 ------------------------------------------------------------------------------ + 216 -- | MonadPlus passthrough instance + 217 instance MonadPlus m => MonadPlus (TemplateMonad m) where + 218 mzero = lift mzero + 219 m `mplus` n = TemplateMonad $ \r s -> + 220 runTemplateMonad m r s `mplus` runTemplateMonad n r s + 221 + 222 + 223 ------------------------------------------------------------------------------ + 224 -- | MonadState passthrough instance + 225 instance MonadState s m => MonadState s (TemplateMonad m) where + 226 get = lift get + 227 put = lift . put + 228 + 229 + 230 ------------------------------------------------------------------------------ + 231 -- | MonadReader passthrough instance + 232 instance MonadReader r m => MonadReader r (TemplateMonad m) where + 233 ask = TemplateMonad $ \_ s -> do + 234 r <- ask + 235 return (r,s) + 236 local f (TemplateMonad m) = + 237 TemplateMonad $ \r s -> local f (m r s) + 238 + 239 + 240 ------------------------------------------------------------------------------ + 241 -- | Helper for MonadError instance. + 242 liftCatch :: (m (a,TemplateState m) + 243 -> (e -> m (a,TemplateState m)) + 244 -> m (a,TemplateState m)) + 245 -> TemplateMonad m a + 246 -> (e -> TemplateMonad m a) + 247 -> TemplateMonad m a + 248 liftCatch ce m h = + 249 TemplateMonad $ \r s -> + 250 (runTemplateMonad m r s `ce` + 251 (\e -> runTemplateMonad (h e) r s)) + 252 + 253 + 254 ------------------------------------------------------------------------------ + 255 -- | MonadError passthrough instance + 256 instance (MonadError e m) => MonadError e (TemplateMonad m) where + 257 throwError = lift . throwError + 258 catchError = liftCatch catchError + 259 + 260 + 261 ------------------------------------------------------------------------------ + 262 -- | Helper for MonadCont instance. + 263 liftCallCC :: ((((a,TemplateState m) -> m (b, TemplateState m)) + 264 -> m (a, TemplateState m)) + 265 -> m (a, TemplateState m)) + 266 -> ((a -> TemplateMonad m b) -> TemplateMonad m a) + 267 -> TemplateMonad m a + 268 liftCallCC ccc f = TemplateMonad $ \r s -> + 269 ccc $ \c -> + 270 runTemplateMonad (f (\a -> TemplateMonad $ \_ _ -> c (a, s))) r s + 271 + 272 + 273 ------------------------------------------------------------------------------ + 274 -- | MonadCont passthrough instance + 275 instance (MonadCont m) => MonadCont (TemplateMonad m) where + 276 callCC = liftCallCC callCC + 277 + 278 + 279 ------------------------------------------------------------------------------ + 280 -- | The Typeable instance is here so Heist can be dynamically executed with + 281 -- Hint. + 282 instance (Typeable1 m, Typeable a) => Typeable (TemplateMonad m a) where + 283 typeOf _ = mkTyConApp tCon [mRep, aRep] + 284 where + 285 tCon = mkTyCon "TemplateMonad" + 286 maRep = typeOf (undefined :: m a) + 287 (mCon, [aRep]) = splitTyConApp maRep + 288 mRep = mkTyConApp mCon [] + 289 + 290 + 291 ------------------------------------------------------------------------------ + 292 -- Functions for our monad. + 293 ------------------------------------------------------------------------------ + 294 + 295 + 296 ------------------------------------------------------------------------------ + 297 -- | Gets the node currently being processed. + 298 -- + 299 -- > <speech author="Shakespeare"> + 300 -- > To sleep, perchance to dream. + 301 -- > </speech> + 302 -- + 303 -- When you call @getParamNode@ inside the code for the @speech@ splice, it + 304 -- returns the Node for the @speech@ tag and its children. @getParamNode >>= + 305 -- getChildren@ returns a list containing one 'Text' node containing part of + 306 -- Hamlet's speech. @getParamNode >>= getAttribute \"author\"@ would return + 307 -- @Just "Shakespeare"@. + 308 getParamNode :: Monad m => TemplateMonad m Node + 309 getParamNode = TemplateMonad $ \r s -> return (r,s) + 310 + 311 + 312 ------------------------------------------------------------------------------ + 313 -- | TemplateMonad's local + 314 localParamNode :: Monad m + 315 => (Node -> Node) + 316 -> TemplateMonad m a + 317 -> TemplateMonad m a + 318 localParamNode f m = TemplateMonad $ \r s -> runTemplateMonad m (f r) s + 319 + 320 + 321 ------------------------------------------------------------------------------ + 322 -- | TemplateMonad's gets + 323 getsTS :: Monad m => (TemplateState m -> r) -> TemplateMonad m r + 324 getsTS f = TemplateMonad $ \_ s -> return (f s, s) + 325 + 326 + 327 ------------------------------------------------------------------------------ + 328 -- | TemplateMonad's get + 329 getTS :: Monad m => TemplateMonad m (TemplateState m) + 330 getTS = TemplateMonad $ \_ s -> return (s, s) + 331 + 332 + 333 ------------------------------------------------------------------------------ + 334 -- | TemplateMonad's put + 335 putTS :: Monad m => TemplateState m -> TemplateMonad m () + 336 putTS s = TemplateMonad $ \_ _ -> return ((), s) + 337 + 338 + 339 ------------------------------------------------------------------------------ + 340 -- | TemplateMonad's modify + 341 modifyTS :: Monad m + 342 => (TemplateState m -> TemplateState m) + 343 -> TemplateMonad m () + 344 modifyTS f = TemplateMonad $ \_ s -> return ((), f s) + 345 + 346 + ++ diff --git a/static/docs/heist-hpc/Text.Templating.Heist.hs.html b/static/docs/heist-hpc/Text.Templating.Heist.hs.html index 6bb3209..83efca2 100644 --- a/static/docs/heist-hpc/Text.Templating.Heist.hs.html +++ b/static/docs/heist-hpc/Text.Templating.Heist.hs.html @@ -75,8 +75,8 @@ 64 ( 65 -- * Types 66 Node - 67 , Splice - 68 , Template + 67 , Template + 68 , Splice 69 , TemplateMonad 70 , TemplateState 71 @@ -101,59 +101,55 @@ 90 , getContext 91 92 -- * Functions for running splices and templates - 93 , runTemplate - 94 , evalTemplate - 95 , callTemplate - 96 , renderTemplate - 97 , bindStrings - 98 - 99 -- * Misc functions - 100 , runSplice - 101 , runRawTemplate - 102 , getDoc - 103 , bindStaticTag - 104 - 105 , heistExpatOptions - 106 , module Text.Templating.Heist.Constants - 107 ) where - 108 - 109 import Control.Monad.Trans - 110 import qualified Data.Map as Map - 111 import Text.Templating.Heist.Internal - 112 import Text.Templating.Heist.Constants - 113 import Text.Templating.Heist.Splices - 114 - 115 - 116 ------------------------------------------------------------------------------ - 117 -- | The default set of built-in splices. - 118 defaultSpliceMap :: MonadIO m => SpliceMap m - 119 defaultSpliceMap = Map.fromList - 120 [(applyTag, applyImpl) - 121 ,(bindTag, bindImpl) - 122 ,(ignoreTag, ignoreImpl) - 123 ,(markdownTag, markdownSplice) - 124 ] - 125 - 126 - 127 ------------------------------------------------------------------------------ - 128 -- | An empty template state, with Heist's default splices (@\<bind\>@ and - 129 -- @\<apply\>@) mapped. - 130 emptyTemplateState :: MonadIO m => TemplateState m - 131 emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 - 132 return return return - 133 - 134 - 135 -- $hookDoc - 136 -- Heist hooks allow you to modify templates when they are loaded and before - 137 -- and after they are run. Every time you call one of the addAbcHook - 138 -- functions the hook is added to onto the processing pipeline. The hooks - 139 -- processes the template in the order that they were added to the - 140 -- TemplateState. - 141 -- - 142 -- The pre-run and post-run hooks are run before and after every template is - 143 -- run/rendered. You should be careful what code you put in these hooks - 144 -- because it can significantly affect the performance of your site. - 145 + 93 , evalTemplate + 94 , callTemplate + 95 , renderTemplate + 96 , bindStrings + 97 + 98 -- * Misc functions + 99 , getDoc + 100 , bindStaticTag + 101 + 102 ) where + 103 + 104 import Control.Monad.Trans + 105 import qualified Data.Map as Map + 106 import Text.Templating.Heist.Internal + 107 import Text.Templating.Heist.Splices + 108 import Text.Templating.Heist.Types + 109 + 110 + 111 ------------------------------------------------------------------------------ + 112 -- | The default set of built-in splices. + 113 defaultSpliceMap :: MonadIO m => SpliceMap m + 114 defaultSpliceMap = Map.fromList + 115 [(applyTag, applyImpl) + 116 ,(bindTag, bindImpl) + 117 ,(ignoreTag, ignoreImpl) + 118 ,(markdownTag, markdownSplice) + 119 ] + 120 + 121 + 122 ------------------------------------------------------------------------------ + 123 -- | An empty template state, with Heist's default splices (@\<apply\>@, + 124 -- @\<bind\>@, @\<ignore\>@, and @\<markdown\>@) mapped. The static tag is + 125 -- not mapped here because it must be mapped manually in your application. + 126 emptyTemplateState :: MonadIO m => TemplateState m + 127 emptyTemplateState = TemplateState defaultSpliceMap Map.empty True [] 0 + 128 return return return [] + 129 + 130 + 131 -- $hookDoc + 132 -- Heist hooks allow you to modify templates when they are loaded and before + 133 -- and after they are run. Every time you call one of the addAbcHook + 134 -- functions the hook is added to onto the processing pipeline. The hooks + 135 -- processes the template in the order that they were added to the + 136 -- TemplateState. + 137 -- + 138 -- The pre-run and post-run hooks are run before and after every template is + 139 -- run/rendered. You should be careful what code you put in these hooks + 140 -- because it can significantly affect the performance of your site. + 141 diff --git a/static/docs/heist-hpc/hpc_index.html b/static/docs/heist-hpc/hpc_index.html index b1e5f6d..56875d0 100644 --- a/static/docs/heist-hpc/hpc_index.html +++ b/static/docs/heist-hpc/hpc_index.html @@ -8,19 +8,19 @@
module | Top Level Definitions | Alternatives | Expressions | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
% | covered / total | % | covered / total | % | covered / total | ||||||||||
module Text.Templating.Heist | -100% | 2/2 | - | 0/0 | 91% | 21/23 | 100% | 2/2 | - | 0/0 | 91% | 22/24 | |||
module Text.Templating.Heist.Constants | -0% | 0/1 | - | 0/0 | 0% | 0/740 | 50% | 1/2 | - | 0/0 | 0% | 5/749 | |||
module Text.Templating.Heist.Internal | -70% | 29/41 | 75% | 15/20 | 62% | 314/501 | 79% | 38/48 | 77% | 24/31 | 78% | 480/609 | |||
module Text.Templating.Heist.Splices | - | 0/0 | - | 0/0 | - | 0/0 | |||||||||
module Text.Templating.Heist.Splices.Apply | -100% | 3/3 | 50% | 2/4 | 84% | 42/50 | 100% | 3/3 | 100% | 4/4 | 98% | 57/58 | |||
module Text.Templating.Heist.Splices.Bind | 100% | 3/3 | - | 0/0 | 91% | 21/23 | 0% | 0/3 | 0% | 0/6 | 0% | 0/134 | |||
module Text.Templating.Heist.Tests | -80% | 32/40 | 57% | 12/21 | 75% | 479/633 | 79% | 35/44 | 57% | 12/21 | 77% | 582/755 | |||
module Text.Templating.Heist.Types | +35% | 13/37 | - | 0/0 | 44% | 120/270 | |||||||||
Program Coverage Total | -68% | 71/104 | 49% | 29/59 | 38% | 879/2280 | 63% | 97/153 | 57% | 40/70 | 46% | 1289/2798 |
module | Top Level Definitions | Alternatives | Expressions | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
% | covered / total | % | covered / total | % | covered / total | ||||||||||
module Text.Templating.Heist.Splices.Apply | +100% | 3/3 | 100% | 4/4 | 98% | 57/58 | |||||||||
module Text.Templating.Heist.Internal | -70% | 29/41 | 75% | 15/20 | 62% | 314/501 | 79% | 38/48 | 77% | 24/31 | 78% | 480/609 | |||
module Text.Templating.Heist.Tests | -80% | 32/40 | 57% | 12/21 | 75% | 479/633 | |||||||||
module Text.Templating.Heist.Splices.Apply | -100% | 3/3 | 50% | 2/4 | 84% | 42/50 | 79% | 35/44 | 57% | 12/21 | 77% | 582/755 | |||
module Text.Templating.Heist.Splices.Static | 0% | 0/3 | 0% | 0/6 | 0% | 0/134 | module Text.Templating.Heist.Splices.Markdown | 11% | 1/9 | 0% | 0/8 | 0% | 1/173 | ||
module Text.Templating.Heist.Types | +35% | 13/37 | - | 0/0 | 44% | 120/270 | |||||||||
module Text.Templating.Heist.Constants | -0% | 0/1 | - | 0/0 | 0% | 0/740 | 50% | 1/2 | - | 0/0 | 0% | 5/749 | |||
module Text.Templating.Heist | -100% | 2/2 | - | 0/0 | 91% | 21/23 | 100% | 2/2 | - | 0/0 | 91% | 22/24 | |||
module Text.Templating.Heist.Splices | - | 0/0 | - | 0/0 | - | 0/0 | 100% | 3/3 | - | 0/0 | 91% | 21/23 | |||
Program Coverage Total | -68% | 71/104 | 49% | 29/59 | 38% | 879/2280 | 63% | 97/153 | 57% | 40/70 | 46% | 1289/2798 |
module | Top Level Definitions | Alternatives | Expressions | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
% | covered / total | % | covered / total | % | covered / total | ||||||||||
module Text.Templating.Heist.Splices.Apply | +100% | 3/3 | 100% | 4/4 | 98% | 57/58 | |||||||||
module Text.Templating.Heist | -100% | 2/2 | - | 0/0 | 91% | 21/23 | 100% | 2/2 | - | 0/0 | 91% | 22/24 | |||
module Text.Templating.Heist.Splices.Bind | 100% | 3/3 | - | 0/0 | 91% | 21/23 | |||||||||
module Text.Templating.Heist.Splices.Apply | -100% | 3/3 | 50% | 2/4 | 84% | 42/50 | module Text.Templating.Heist.Internal | +79% | 38/48 | 77% | 24/31 | 78% | 480/609 | ||
module Text.Templating.Heist.Tests | -80% | 32/40 | 57% | 12/21 | 75% | 479/633 | 79% | 35/44 | 57% | 12/21 | 77% | 582/755 | |||
module Text.Templating.Heist.Internal | -70% | 29/41 | 75% | 15/20 | 62% | 314/501 | module Text.Templating.Heist.Types | +35% | 13/37 | - | 0/0 | 44% | 120/270 | ||
module Text.Templating.Heist.Splices.Ignore | 50% | 1/2 | - | 0/0 | 33% | 1/3 | |||||||||
module Text.Templating.Heist.Constants | -0% | 0/1 | - | 0/0 | 0% | 0/740 | 50% | 1/2 | - | 0/0 | 0% | 5/749 | |||
module Text.Templating.Heist.Splices.Static | 0% | 0/3 | 0% | 0/6 | 0% | 0/134 | - | 0/0 | - | 0/0 | - | 0/0 | |||
Program Coverage Total | -68% | 71/104 | 49% | 29/59 | 38% | 879/2280 | 63% | 97/153 | 57% | 40/70 | 46% | 1289/2798 |
module | Top Level Definitions | Alternatives | Expressions | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
% | covered / total | % | covered / total | % | covered / total | ||||||||||
module Text.Templating.Heist.Splices.Apply | +100% | 3/3 | 100% | 4/4 | 98% | 57/58 | |||||||||
module Text.Templating.Heist | -100% | 2/2 | - | 0/0 | 91% | 21/23 | 100% | 2/2 | - | 0/0 | 91% | 22/24 | |||
module Text.Templating.Heist.Splices.Bind | 100% | 3/3 | - | 0/0 | 91% | 21/23 | |||||||||
module Text.Templating.Heist.Splices.Apply | -100% | 3/3 | 50% | 2/4 | 84% | 42/50 | |||||||||
module Text.Templating.Heist.Tests | -80% | 32/40 | 57% | 12/21 | 75% | 479/633 | 79% | 35/44 | 57% | 12/21 | 77% | 582/755 | |||
module Text.Templating.Heist.Internal | -70% | 29/41 | 75% | 15/20 | 62% | 314/501 | 79% | 38/48 | 77% | 24/31 | 78% | 480/609 | |||
module Text.Templating.Heist.Constants | +50% | 1/2 | - | 0/0 | 0% | 5/749 | |||||||||
module Text.Templating.Heist.Splices.Ignore | 50% | 1/2 | - | 0/0 | 33% | 1/3 | |||||||||
module Text.Templating.Heist.Types | +35% | 13/37 | - | 0/0 | 44% | 120/270 | |||||||||
module Text.Templating.Heist.Splices.Markdown | 11% | 1/9 | 0% | 0/8 | 0% | 1/173 | |||||||||
module Text.Templating.Heist.Constants | -0% | 0/1 | - | 0/0 | 0% | 0/740 | |||||||||
module Text.Templating.Heist.Splices.Static | 0% | 0/3 | 0% | 0/6 | 0% | 0/134 | |||||||||
- | 0/0 | - | 0/0 | - | 0/0 | ||||||||||
Program Coverage Total | -68% | 71/104 | 49% | 29/59 | 38% | 879/2280 | 63% | 97/153 | 57% | 40/70 | 46% | 1289/2798 |