Proper cleanPath behavior
This commit is contained in:
parent
3003c9b3cd
commit
8684ce5b27
@ -15,6 +15,7 @@ import Network.Wai.Test
|
|||||||
data Y = Y
|
data Y = Y
|
||||||
mkYesod "Y" [$parseRoutes|
|
mkYesod "Y" [$parseRoutes|
|
||||||
/foo FooR GET
|
/foo FooR GET
|
||||||
|
/foo/#String FooStringR GET
|
||||||
/bar BarR GET
|
/bar BarR GET
|
||||||
|]
|
|]
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ instance Yesod Y where
|
|||||||
corrected = filter (not . null) s
|
corrected = filter (not . null) s
|
||||||
|
|
||||||
getFooR = return $ RepPlain "foo"
|
getFooR = return $ RepPlain "foo"
|
||||||
|
getFooStringR = return . RepPlain . toContent
|
||||||
getBarR = return $ RepPlain "bar"
|
getBarR = return $ RepPlain "bar"
|
||||||
|
|
||||||
cleanPathTest :: Test
|
cleanPathTest :: Test
|
||||||
@ -38,6 +40,7 @@ cleanPathTest = testGroup "Test.CleanPath"
|
|||||||
, testCase "noTrailingSlash" noTrailingSlash
|
, testCase "noTrailingSlash" noTrailingSlash
|
||||||
, testCase "add trailing slash" addTrailingSlash
|
, testCase "add trailing slash" addTrailingSlash
|
||||||
, testCase "has trailing slash" hasTrailingSlash
|
, testCase "has trailing slash" hasTrailingSlash
|
||||||
|
, testCase "/foo/something" fooSomething
|
||||||
]
|
]
|
||||||
|
|
||||||
runner f = toWaiApp Y >>= runSession f
|
runner f = toWaiApp Y >>= runSession f
|
||||||
@ -77,3 +80,11 @@ hasTrailingSlash = runner $ do
|
|||||||
assertStatus 200 res
|
assertStatus 200 res
|
||||||
assertContentType "text/plain; charset=utf-8" res
|
assertContentType "text/plain; charset=utf-8" res
|
||||||
assertBody "bar" res
|
assertBody "bar" res
|
||||||
|
|
||||||
|
fooSomething = runner $ do
|
||||||
|
res <- request defaultRequest
|
||||||
|
{ pathInfo = "/foo/something"
|
||||||
|
}
|
||||||
|
assertStatus 200 res
|
||||||
|
assertContentType "text/plain; charset=utf-8" res
|
||||||
|
assertBody "something" res
|
||||||
|
|||||||
@ -20,6 +20,7 @@ module Yesod.Dispatch
|
|||||||
, toWaiAppPlain
|
, toWaiAppPlain
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Either (partitionEithers)
|
||||||
import Prelude hiding (exp)
|
import Prelude hiding (exp)
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
import Yesod.Handler
|
import Yesod.Handler
|
||||||
@ -35,7 +36,6 @@ import Network.Wai.Middleware.Jsonp
|
|||||||
import Network.Wai.Middleware.Gzip
|
import Network.Wai.Middleware.Gzip
|
||||||
|
|
||||||
import qualified Data.ByteString.Char8 as B
|
import qualified Data.ByteString.Char8 as B
|
||||||
import qualified Data.ByteString as S
|
|
||||||
import Data.ByteString.Lazy.Char8 ()
|
import Data.ByteString.Lazy.Char8 ()
|
||||||
|
|
||||||
import Web.ClientSession
|
import Web.ClientSession
|
||||||
@ -116,7 +116,14 @@ mkYesodGeneral name args clazzes isSub res = do
|
|||||||
[ FunD (mkName "renderRoute") render
|
[ FunD (mkName "renderRoute") render
|
||||||
]
|
]
|
||||||
|
|
||||||
yd <- mkYesodDispatch' th'
|
let splitter :: (THResource, Maybe String)
|
||||||
|
-> Either
|
||||||
|
(THResource, Maybe String)
|
||||||
|
(THResource, Maybe String)
|
||||||
|
splitter a@((_, SubSite{}), _) = Left a
|
||||||
|
splitter a = Right a
|
||||||
|
let (resSub, resLoc) = partitionEithers $ map splitter th'
|
||||||
|
yd <- mkYesodDispatch' resSub resLoc
|
||||||
let master = mkName "master"
|
let master = mkName "master"
|
||||||
let ctx = if isSub
|
let ctx = if isSub
|
||||||
then ClassP (mkName "Yesod") [VarT master] : clazzes
|
then ClassP (mkName "Yesod") [VarT master] : clazzes
|
||||||
@ -174,22 +181,6 @@ toWaiApp' y key' env = do
|
|||||||
let dropSlash ('/':x) = x
|
let dropSlash ('/':x) = x
|
||||||
dropSlash x = x
|
dropSlash x = x
|
||||||
let segments = decodePathInfo $ dropSlash $ B.unpack $ W.pathInfo env
|
let segments = decodePathInfo $ dropSlash $ B.unpack $ W.pathInfo env
|
||||||
-- FIXME cleanPath will not force redirect if yesodDispatch likes its arguments
|
|
||||||
case yesodDispatch y key' segments y id of
|
case yesodDispatch y key' segments y id of
|
||||||
Just app -> app env
|
Just app -> app env
|
||||||
Nothing ->
|
Nothing -> yesodRunner y y id key' Nothing notFound env
|
||||||
case cleanPath y segments of
|
|
||||||
Right segments' ->
|
|
||||||
case yesodDispatch y key' segments' y id of
|
|
||||||
Just app -> app env
|
|
||||||
Nothing -> yesodRunner y y id key' Nothing notFound env
|
|
||||||
Left segments' ->
|
|
||||||
let dest = joinPath y (approot y) segments' []
|
|
||||||
dest' =
|
|
||||||
if S.null (W.queryString env)
|
|
||||||
then dest
|
|
||||||
else dest ++ '?' : B.unpack (W.queryString env)
|
|
||||||
in return $ W.responseLBS W.status301
|
|
||||||
[ ("Content-Type", "text/plain")
|
|
||||||
, ("Location", B.pack $ dest')
|
|
||||||
] "Redirecting"
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
-- | A bunch of Template Haskell used in the Yesod.Dispatch module.
|
-- | A bunch of Template Haskell used in the Yesod.Dispatch module.
|
||||||
module Yesod.Internal.Dispatch
|
module Yesod.Internal.Dispatch
|
||||||
( mkYesodDispatch'
|
( mkYesodDispatch'
|
||||||
@ -17,6 +18,9 @@ import Yesod.Core (yesodRunner, yesodDispatch)
|
|||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
||||||
import Data.Char (toLower)
|
import Data.Char (toLower)
|
||||||
import qualified Data.ByteString.Char8 as S8
|
import qualified Data.ByteString.Char8 as S8
|
||||||
|
import Data.ByteString.Lazy.Char8 ()
|
||||||
|
import qualified Data.ByteString as S
|
||||||
|
import Yesod.Core (Yesod (joinPath, approot, cleanPath))
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
|
|
||||||
@ -64,16 +68,52 @@ case segments of
|
|||||||
Obviously we would never want to write code by hand like this, but generating it is not too bad.
|
Obviously we would never want to write code by hand like this, but generating it is not too bad.
|
||||||
|
|
||||||
This function generates a clause for the yesodDispatch function based on a set of routes.
|
This function generates a clause for the yesodDispatch function based on a set of routes.
|
||||||
|
|
||||||
|
NOTE: We deal with subsites first; if none of those match, we try to apply
|
||||||
|
cleanPath. If that indicates a redirect, we perform it. Otherwise, we match
|
||||||
|
local routes.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
mkYesodDispatch' :: [((String, Pieces), Maybe String)] -> Q Clause
|
|
||||||
mkYesodDispatch' res = do
|
sendRedirect :: Yesod master => master -> [String] -> W.Application
|
||||||
|
sendRedirect y segments' env =
|
||||||
|
return $ W.responseLBS W.status301
|
||||||
|
[ ("Content-Type", "text/plain")
|
||||||
|
, ("Location", S8.pack $ dest')
|
||||||
|
] "Redirecting"
|
||||||
|
where
|
||||||
|
dest = joinPath y (approot y) segments' []
|
||||||
|
dest' =
|
||||||
|
if S.null (W.queryString env)
|
||||||
|
then dest
|
||||||
|
else dest ++ '?' : S8.unpack (W.queryString env)
|
||||||
|
|
||||||
|
mkYesodDispatch' :: [((String, Pieces), Maybe String)]
|
||||||
|
-> [((String, Pieces), Maybe String)]
|
||||||
|
-> Q Clause
|
||||||
|
mkYesodDispatch' resSub resLoc = do
|
||||||
sub <- newName "sub"
|
sub <- newName "sub"
|
||||||
master <- newName "master"
|
master <- newName "master"
|
||||||
mkey <- newName "mkey"
|
mkey <- newName "mkey"
|
||||||
segments <- newName "segments"
|
segments <- newName "segments"
|
||||||
|
segments' <- newName "segmentsClean"
|
||||||
toMasterRoute <- newName "toMasterRoute"
|
toMasterRoute <- newName "toMasterRoute"
|
||||||
nothing <- [|Nothing|]
|
nothing <- [|Nothing|]
|
||||||
body <- foldM (go master (VarE sub) (VarE toMasterRoute) mkey segments) nothing res
|
bodyLoc <- foldM (go master (VarE sub) (VarE toMasterRoute) mkey segments') nothing resLoc
|
||||||
|
cp <- [|cleanPath|]
|
||||||
|
sr <- [|sendRedirect|]
|
||||||
|
just <- [|Just|]
|
||||||
|
let bodyLoc' =
|
||||||
|
CaseE (cp `AppE` VarE master `AppE` VarE segments)
|
||||||
|
[ Match (ConP (mkName "Left") [VarP segments'])
|
||||||
|
(NormalB $ just `AppE`
|
||||||
|
(sr `AppE` VarE master `AppE` VarE segments'))
|
||||||
|
[]
|
||||||
|
, Match (ConP (mkName "Right") [VarP segments'])
|
||||||
|
(NormalB bodyLoc)
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
body <- foldM (go master (VarE sub) (VarE toMasterRoute) mkey segments) bodyLoc' resSub
|
||||||
return $ Clause
|
return $ Clause
|
||||||
[VarP sub, VarP mkey, VarP segments, VarP master, VarP toMasterRoute]
|
[VarP sub, VarP mkey, VarP segments, VarP master, VarP toMasterRoute]
|
||||||
(NormalB body)
|
(NormalB body)
|
||||||
|
|||||||
@ -33,7 +33,7 @@ library
|
|||||||
, bytestring >= 0.9.1.4 && < 0.10
|
, bytestring >= 0.9.1.4 && < 0.10
|
||||||
, text >= 0.5 && < 0.12
|
, text >= 0.5 && < 0.12
|
||||||
, template-haskell
|
, template-haskell
|
||||||
, web-routes-quasi >= 0.6.3 && < 0.7
|
, web-routes-quasi >= 0.6.3.1 && < 0.7
|
||||||
, hamlet >= 0.7 && < 0.8
|
, hamlet >= 0.7 && < 0.8
|
||||||
, blaze-builder >= 0.2.1 && < 0.3
|
, blaze-builder >= 0.2.1 && < 0.3
|
||||||
, transformers >= 0.2 && < 0.3
|
, transformers >= 0.2 && < 0.3
|
||||||
@ -74,6 +74,7 @@ executable runtests
|
|||||||
test-framework-quickcheck2,
|
test-framework-quickcheck2,
|
||||||
test-framework-hunit,
|
test-framework-hunit,
|
||||||
HUnit,
|
HUnit,
|
||||||
|
wai-test,
|
||||||
QuickCheck >= 2 && < 3
|
QuickCheck >= 2 && < 3
|
||||||
else
|
else
|
||||||
Buildable: False
|
Buildable: False
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user