on master help sign in
Merge remote-tracking branch 'origin/master' into beta

Conflicts: wai-extra/Network/Wai/Parse.hs wai-extra/wai-extra.cabal

commitc05721037c292a9a5ea24f980d7008195d3cbaa5 authorMichael Snoyman <michael@snoyman.com> dateThu 21 Jun 2012, 07:04 (14 years ago) parent2d2582cb448ef8
4 files changed+36−33
wai-extra/Network/Wai/Parse.hs+9−30 wai-extra/test/WaiExtraTest.hs+24−1 wai-extra/tests.hs+1−1 wai-extra/wai-extra.cabal+2−1
wai-extra/Network/Wai/Parse.hs+9−30
@@ -25,11 +25,11 @@ module Network.Wai.Parse
2525#endif
2626 ) where
2727
28+import qualified Data.ByteString.Search as Search
2829import qualified Data.ByteString as S
2930import qualified Data.ByteString.Lazy as L
3031import qualified Data.ByteString.Char8 as S8
3132import Data.Word (Word8)
32−import Data.Bits
3333import Data.Maybe (fromMaybe)
3434import Data.List (sortBy)
3535import Data.Function (on)
@@ -46,38 +46,11 @@ import Control.Monad (when, unless)
4646import Control.Monad.Trans.Class (lift)
4747import Control.Monad.Trans.Resource (allocate, release, register)
4848
49−uncons :: S.ByteString -> Maybe (Word8, S.ByteString)
50−uncons s
51− | S.null s = Nothing
52− | otherwise = Just (S.head s, S.tail s)
53−
5449breakDiscard :: Word8 -> S.ByteString -> (S.ByteString, S.ByteString)
5550breakDiscard w s =
5651 let (x, y) = S.break (== w) s
5752 in (x, S.drop 1 y)
5853
59−qsDecode :: S.ByteString -> S.ByteString
60−qsDecode z = fst $ S.unfoldrN (S.length z) go z
61− where
62− go bs =
63− case uncons bs of
64− Nothing -> Nothing
65− Just (43, ws) -> Just (32, ws) -- plus to space
66− Just (37, ws) -> Just $ fromMaybe (37, ws) $ do -- percent
67− (x, xs) <- uncons ws
68− x' <- hexVal x
69− (y, ys) <- uncons xs
70− y' <- hexVal y
71− Just $ (combine x' y', ys)
72− Just (w, ws) -> Just (w, ws)
73− hexVal w
74− | 48 <= w && w <= 57 = Just $ w - 48 -- 0 - 9
75− | 65 <= w && w <= 70 = Just $ w - 55 -- A - F
76− | 97 <= w && w <= 102 = Just $ w - 87 -- a - f
77− | otherwise = Nothing
78− combine :: Word8 -> Word8 -> Word8
79− combine a b = shiftL a 4 .|. b
80−
8154-- | Parse the HTTP accept string to determine supported content types.
8255parseHttpAccept :: S.ByteString -> [S.ByteString]
8356parseHttpAccept = map fst
@@ -226,7 +199,7 @@ parsePieces sink bound =
226199 let iter front bs = return $ front . (:) bs
227200 (wasFound, front) <- sinkTillBound bound iter seed
228201 let bs = S.concat $ front []
229− let x' = (name, qsDecode bs)
202+ let x' = (name, bs)
230203 yield $ Left x'
231204 when wasFound loop
232205 _ -> do
@@ -248,8 +221,14 @@ data Bound = FoundBound S.ByteString S.ByteString
248221 deriving (Eq, Show)
249222
250223findBound :: S.ByteString -> S.ByteString -> Bound
251−findBound b bs = go [0..S.length bs - 1]
224+findBound b bs = handleBreak $ Search.breakOn b bs
252225 where
226+ handleBreak (h, t)
227+ | S.null t = go [lowBound..S.length bs - 1]
228+ | otherwise = FoundBound h $ S.drop (S.length b) t
229+
230+ lowBound = max 0 $ S.length bs - S.length b
231+
253232 go [] = NoBound
254233 go (i:is)
255234 | mismatch [0..S.length b - 1] [i..S.length bs - 1] = go is
wai-extra/test/WaiExtraTest.hs+24−1
@@ -33,13 +33,15 @@ import Control.Monad.IO.Class (liftIO)
3333import Data.Maybe (fromMaybe)
3434import Network.HTTP.Types (parseSimpleQuery, status200)
3535
36−specs :: Specs
36+specs :: Spec
3737specs = do
3838 describe "Network.Wai.Parse" $ do
3939 it "parseQueryString" caseParseQueryString
4040 it "parseQueryString with question mark" caseParseQueryStringQM
4141 it "parseHttpAccept" caseParseHttpAccept
4242 it "parseRequestBody" caseParseRequestBody
43+ it "multipart with plus" caseMultipartPlus
44+ it "urlencoded with plus" caseUrlEncPlus
4345 {-
4446 , it "findBound" caseFindBound
4547 , it "sinkTillBound" caseSinkTillBound
@@ -169,6 +171,27 @@ caseParseRequestBody =
169171 expected3
170172 result3'
171173
174+caseMultipartPlus :: Assertion
175+caseMultipartPlus = do
176+ result <- C.runResourceT $ parseRequestBody' lbsSink $ toRequest ctype content
177+ liftIO $ result @?= ([("email", "has+plus")], [])
178+ where
179+ content = S8.pack $
180+ "--AaB03x\n" ++
181+ "Content-Disposition: form-data; name=\"email\"\n" ++
182+ "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
183+ "has+plus\n" ++
184+ "--AaB03x--"
185+ ctype = "multipart/form-data; boundary=AaB03x"
186+
187+caseUrlEncPlus :: Assertion
188+caseUrlEncPlus = do
189+ result <- C.runResourceT $ parseRequestBody' lbsSink $ toRequest ctype content
190+ liftIO $ result @?= ([("email", "has+plus")], [])
191+ where
192+ content = S8.pack $ "email=has%2Bplus"
193+ ctype = "application/x-www-form-urlencoded"
194+
172195toRequest :: S8.ByteString -> S8.ByteString -> SRequest
173196toRequest ctype content = SRequest defaultRequest
174197 { requestHeaders = [("Content-Type", ctype)]
wai-extra/tests.hs+1−1
@@ -2,4 +2,4 @@ import Test.Hspec.Monadic
22import qualified WaiExtraTest
33
44main :: IO ()
5−main = hspecX WaiExtraTest.specs
5+main = hspec WaiExtraTest.specs
wai-extra/wai-extra.cabal+2−1
@@ -40,6 +40,7 @@ Library
4040 , ansi-terminal
4141 , resourcet >= 0.3 && < 0.4
4242 , void >= 0.5 && < 0.6
43+ , stringsearch >= 0.3 && < 0.4
4344
4445 Exposed-modules: Network.Wai.Handler.CGI
4546 Network.Wai.Middleware.AcceptOverride
@@ -73,7 +74,7 @@ test-suite tests
7374 , zlib
7475 , text
7576 , bytestring
76− , directory
77+ , directory
7778 , zlib-bindings
7879 , blaze-builder >= 0.2.1.4 && < 0.4
7980 , data-default