X-Git-Url: http://git.cielonegro.org/gitweb.cgi?a=blobdiff_plain;f=Network%2FHTTP%2FLucu%2FDispatcher%2FInternal.hs;h=35f2f1ed403c81dfa36052d23b20b74995867628;hb=b42b4ab2f78fa62dd023acd136085dae6dc9028a;hp=f8f3b12e328df7f2f9d3057bf84f9d4c4c50963f;hpb=667baf9f664ccc093241287ad727b2839290f456;p=Lucu.git diff --git a/Network/HTTP/Lucu/Dispatcher/Internal.hs b/Network/HTTP/Lucu/Dispatcher/Internal.hs index f8f3b12..35f2f1e 100644 --- a/Network/HTTP/Lucu/Dispatcher/Internal.hs +++ b/Network/HTTP/Lucu/Dispatcher/Internal.hs @@ -1,11 +1,11 @@ {-# LANGUAGE - DoAndIfThenElse - , ExistentialQuantification + ExistentialQuantification + , FlexibleContexts , FlexibleInstances + , GeneralizedNewtypeDeriving , OverlappingInstances , MultiParamTypeClasses - , RecordWildCards - , ScopedTypeVariables + , TemplateHaskell , UndecidableInstances , UnicodeSyntax #-} @@ -18,6 +18,8 @@ module Network.HTTP.Lucu.Dispatcher.Internal , ResourceMap , ResourceTree , ResourceNode + , greedy + , nonGreedy , dispatch ) @@ -71,14 +73,37 @@ class ResourceMapper α where -- |Container type for the 'ResourceMapper' type class. data ResourceMap = ∀α. ResourceMapper α ⇒ RMap α --- FIXME: doc -newtype ResourceTree = Root ResourceNode +-- |'ResourceTree' is an opaque structure which is a map from resource +-- path to 'Resource'. +-- +-- @ +-- 'fromList' [ ([] , 'nonGreedy' '$' 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/stdio.h\" ) -- \/ +-- , ([\"unistd\"], 'nonGreedy' '$' 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/unistd.h\") -- \/unistd +-- ] +-- @ +-- +-- Note that path segments are always represented as octet streams in +-- this system. Lucu automatically decodes percent-encoded URIs but +-- has no involvement in character encodings such as UTF-8, since RFC +-- 2616 (HTTP/1.1) says nothing about character encodings to be used +-- in \"http\" and \"https\" URI schemas. +newtype ResourceTree = Tree (M.Map PathSegments ResourceNode) + deriving Monoid --- FIXME: docs +-- |FIXME: doc data ResourceNode - = Greedy !Resource - | NonGreedy !Resource !ResourceTree - | Branch !ResourceTree + = Greedy { nResource ∷ !Resource } + | NonGreedy { nResource ∷ !Resource } + +-- |FIXME: doc +greedy ∷ Resource → ResourceNode +{-# INLINE CONLIKE greedy #-} +greedy = Greedy + +-- |FIXME: doc +nonGreedy ∷ Resource → ResourceNode +{-# INLINE CONLIKE nonGreedy #-} +nonGreedy = NonGreedy -- Instances of SchemeMapper -------------------------------------------------- instance SchemeMapper SchemeMap where @@ -116,7 +141,7 @@ instance SchemeMapper α ⇒ Unfoldable SchemeMap α where {-# INLINE singleton #-} singleton = schemeMap --- |@a `'mappend'` b@ first tries @a@, and if it fails, tries @b@ next. +-- |@'mappend' a b@ first tries @a@, and if it fails, tries @b@ next. instance Monoid SchemeMap where {-# INLINE mempty #-} mempty = schemeMap e @@ -127,6 +152,7 @@ instance Monoid SchemeMap where {-# INLINE mappend #-} mappend = insert +-- |Any 'Map's from 'Scheme' to 'HostMap' are also 'SchemeMapper's. instance Map α Scheme HostMap ⇒ SchemeMapper α where {-# INLINE findHostMap #-} findHostMap = (maybe (fail (⊥)) return ∘) ∘ lookup @@ -168,7 +194,7 @@ instance HostMapper α ⇒ Unfoldable HostMap α where {-# INLINE singleton #-} singleton = hostMap --- |@a `'mappend'` b@ first tries @a@, and if it fails, tries @b@ next. +-- |@'mappend' a b@ first tries @a@, and if it fails, tries @b@ next. instance Monoid HostMap where {-# INLINE mempty #-} mempty = hostMap e @@ -179,6 +205,7 @@ instance Monoid HostMap where {-# INLINE mappend #-} mappend = insert +-- |Any 'Map's from 'Host' to 'ResourceMap' are also 'HostMapper's. instance Map α Host ResourceMap ⇒ HostMapper α where {-# INLINE findResourceMap #-} findResourceMap = (maybe (fail (⊥)) return ∘) ∘ lookup @@ -213,7 +240,7 @@ instance ResourceMapper α ⇒ Unfoldable ResourceMap α where {-# INLINE singleton #-} singleton = resourceMap --- |@a `'mappend'` b@ first tries @a@, and if it fails, tries @b@ next. +-- |@'mappend' a b@ first tries @a@, and if it fails, tries @b@ next. instance Monoid ResourceMap where {-# INLINE mempty #-} mempty = resourceMap e @@ -224,6 +251,8 @@ instance Monoid ResourceMap where {-# INLINE mappend #-} mappend = insert +-- |Any 'Map's from 'PathSegments' to @('PathSegments', 'Resource')@ +-- are also 'ResourceMapper's. instance Map α PathSegments (PathSegments, Resource) ⇒ ResourceMapper α where {-# INLINE findResource #-} findResource = (maybe (fail (⊥)) return ∘) ∘ lookup @@ -239,57 +268,38 @@ instance ResourceMapper (PathSegments → Maybe (PathSegments, Resource)) where findResource = (maybe (fail (⊥)) return ∘) ∘ flip id -- Instances of ResourceTree -------------------------------------------------- -{- -instance (Functor m, MonadIO m) - ⇒ Unfoldable (ResourceTree m) ([PathSegment], ResourceNode m) where - {-# INLINE insert #-} - insert e (Root root) = Root $ insert e root - {-# INLINE empty #-} - empty = (∅) - -instance (Functor m, MonadIO m) ⇒ Monoid (ResourceTree m) where - {-# INLINE mempty #-} - mempty = Root (∅) - {-# INLINE mappend #-} - mappend (Root a) (Root b) - = Root (a ⊕ b) --} - --- Instances of ResourceNode -------------------------------------------------- -{- -instance (Functor m, MonadIO m) - ⇒ Unfoldable (ResourceNode m) ([PathSegment], ResourceNode m) where +instance Unfoldable ResourceTree (PathSegments, ResourceNode) where {-# INLINEABLE insert #-} - insert (p, a) b = insertNodeAt (canonPath p) a b + insert (p, n) (Tree m) = Tree $ insert (canonPath p, n) m {-# INLINE empty #-} - empty = Branch (∅) + empty = Tree (∅) + {-# INLINE singleton #-} + singleton = Tree ∘ singleton canonPath ∷ (Collection c f, Foldable f e) ⇒ c → c {-# INLINEABLE canonPath #-} canonPath = filter ((¬) ∘ null) -insertNodeAt ∷ (Functor m, MonadIO m) - ⇒ [PathSegment] - → ResourceNode m - → ResourceNode m - → ResourceNode m -{-# INLINEABLE insertNodeAt #-} -insertNodeAt [] a b = a ⊕ b -insertNodeAt (x:[]) a b = Branch (singleton (x, a)) ⊕ b -insertNodeAt (x:xs) a b = insertNodeAt xs a (∅) ⊕ b - -instance (Functor m, MonadIO m) ⇒ Monoid (ResourceNode m) where - {-# INLINE mempty #-} - mempty = Branch (∅) - {-# INLINEABLE mappend #-} - mappend _ (Greedy r ) = Greedy r - mappend (Greedy _ ) (NonGreedy r n) = NonGreedy r n - mappend (NonGreedy _ m) (NonGreedy r n) = NonGreedy r (m ⊕ n) - mappend (Branch m) (NonGreedy r n) = NonGreedy r (m ⊕ n) - mappend (Greedy r ) (Branch _) = Greedy r - mappend (NonGreedy r m) (Branch n) = NonGreedy r (m ⊕ n) - mappend (Branch m) (Branch n) = Branch (m ⊕ n) --} +-- |'findResource' performs the longest prefix match on the tree, +-- finding the most specific one. +instance ResourceMapper ResourceTree where + {-# INLINEABLE findResource #-} + findResource p (Tree m) + = case lookup p m of + Just n → return (p, nResource n) + Nothing → findGreedyResource p m + +findGreedyResource ∷ (Monad m, Map α PathSegments ResourceNode) + ⇒ PathSegments + → α + → MaybeT m (PathSegments, Resource) +findGreedyResource p m + = case back p of + Nothing → fail (⊥) + Just (p', _) → case lookup p' m of + Just (Greedy r) + → return (p', r) + _ → findGreedyResource p' m -- dispatch ------------------------------------------------------------------- dispatch ∷ SchemeMapper α ⇒ URI → α → MaybeT IO (PathSegments, Resource) @@ -297,65 +307,3 @@ dispatch uri = (findResource (uriPathSegments uri) =≪) ∘ (findResourceMap (uriHost uri) =≪) ∘ findHostMap (uriCIScheme uri) - -{- --- |'ResTree' is an opaque structure which is a map from resource path --- to 'Resource'. -newtype ResTree = ResTree ResNode -- root だから Map ではない -type ResSubtree = Map ByteString ResNode -data ResNode = ResNode (Maybe Resource) ResSubtree - --- |'mkResTree' converts a list of @(path, def)@ to a 'ResTree' e.g. --- --- @ --- mkResTree [ ([] , 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/stdio.h\" ) -- \/ --- , ([\"unistd\"], 'Network.HTTP.Lucu.StaticFile.staticFile' \"\/usr\/include\/unistd.h\") -- \/unistd --- ] --- @ --- --- Note that path components are always represented as octet streams --- in this system. Lucu automatically decodes percent-encoded URIs but --- has no involvement in character encodings such as UTF-8, since RFC --- 2616 (HTTP/1.1) says nothing about character encodings to be used --- in \"http\" and \"https\" URI schemas. -mkResTree ∷ [ ([ByteString], Resource) ] → ResTree -mkResTree = processRoot ∘ map (first canonicalisePath) - where - canonicalisePath ∷ [ByteString] → [ByteString] - canonicalisePath = filter ((¬) ∘ BS.null) - - processRoot ∷ [ ([ByteString], Resource) ] → ResTree - processRoot list - = let (roots, nonRoots) = partition (\(path, _) → null path) list - children = processNonRoot nonRoots - in - if null roots then - -- The root has no resources. Maybe there's one at - -- somewhere like "/foo". - ResTree (ResNode Nothing children) - else - -- There is a root resource. - let (_, def) = last roots - in - ResTree (ResNode (Just def) children) - - processNonRoot ∷ [ ([ByteString], Resource) ] → ResSubtree - processNonRoot list - = let subtree = M.fromList [(name, node name) - | name ← childNames] - childNames = [name | (name:_, _) ← list] - node name = let defs = [def | (path, def) ← list, path ≡ [name]] - in - if null defs then - -- No resources are defined - -- here. Maybe there's one at - -- somewhere below this node. - ResNode Nothing children - else - -- There is a resource here. - ResNode (Just $ last defs) children - children = processNonRoot [(path, def) - | (_:path, def) ← list] - in - subtree --} \ No newline at end of file