mirror of
https://github.com/typeable/wai.git
synced 2025-01-07 14:51:40 +03:00
Wrote parseQueryString
This commit is contained in:
parent
30241cfdb1
commit
4c400b98ef
95
Network/Wai/Parse.hs
Normal file
95
Network/Wai/Parse.hs
Normal file
@ -0,0 +1,95 @@
|
||||
-- | Some helpers for parsing data out of a raw WAI 'Request'.
|
||||
|
||||
module Network.Wai.Parse
|
||||
( parseQueryString
|
||||
) where
|
||||
|
||||
import Network.Wai
|
||||
import qualified Data.ByteString as S
|
||||
import Data.Word (Word8)
|
||||
import Data.Bits
|
||||
|
||||
c2w :: Char -> Word8
|
||||
c2w = toEnum . fromEnum
|
||||
|
||||
wequal :: Word8
|
||||
wequal = c2w '='
|
||||
|
||||
wand :: Word8
|
||||
wand = c2w '&'
|
||||
|
||||
wpercent :: Word8
|
||||
wpercent = c2w '%'
|
||||
|
||||
type BufferedBS = ([Word8], S.ByteString)
|
||||
|
||||
uncons :: BufferedBS -> Maybe (Word8, BufferedBS)
|
||||
uncons ((w:ws), s) = Just (w, (ws, s))
|
||||
uncons ([], s)
|
||||
| S.null s = Nothing
|
||||
| otherwise = Just (S.head s, ([], S.tail s))
|
||||
|
||||
cons :: Word8 -> BufferedBS -> BufferedBS
|
||||
cons w (ws, s) = (w:ws, s)
|
||||
|
||||
breakDiscard :: Word8 -> S.ByteString -> (S.ByteString, S.ByteString)
|
||||
breakDiscard w s =
|
||||
let (x, y) = S.break (== w) s
|
||||
in (x, S.drop 1 y)
|
||||
|
||||
-- | Split out the query string into a list of keys and values. A few
|
||||
-- importants points:
|
||||
--
|
||||
-- * There is no way to distinguish between a parameter with no value and a
|
||||
-- parameter with an empty value. Eg, "foo=" and "foo" are the same.
|
||||
--
|
||||
-- * The result returned is still bytestrings, since we perform no character
|
||||
-- decoding here. Most likely, you will want to use UTF-8 decoding, but this is
|
||||
-- left to the user of the library.
|
||||
--
|
||||
-- * Percent decoding errors are ignored. In particular, "%Q" will be output as
|
||||
-- "%Q".
|
||||
parseQueryString :: S.ByteString -> [(S.ByteString, S.ByteString)]
|
||||
parseQueryString q | S.null q = []
|
||||
parseQueryString q =
|
||||
let (x, xs) = breakDiscard wand q
|
||||
in parsePair x : parseQueryString xs
|
||||
where
|
||||
parsePair x =
|
||||
let (k, v) = breakDiscard wequal x
|
||||
in (decode k, decode v)
|
||||
decode x = fst $ S.unfoldrN (S.length x) go (NoPercent, ([], x))
|
||||
go (state, x) =
|
||||
case (state, uncons x) of
|
||||
(NoPercent, Nothing) -> Nothing
|
||||
(NoChar, Nothing) -> Just (wpercent, (NoPercent, x))
|
||||
(OneChar (w, _), Nothing) ->
|
||||
Just (wpercent, (NoPercent, cons w x))
|
||||
(NoPercent, Just (w, ws)) ->
|
||||
if w == wpercent
|
||||
then go (NoChar, ws)
|
||||
else Just (w, (NoPercent, ws))
|
||||
(NoChar, Just (w, ws)) ->
|
||||
case hexVal w of
|
||||
Nothing -> Just (wpercent, (NoPercent, x))
|
||||
Just v -> go (OneChar (w, v), ws)
|
||||
(OneChar (w1, v1), Just (w2, ws)) ->
|
||||
case hexVal w2 of
|
||||
Nothing ->
|
||||
Just (wpercent, (NoPercent, w1 `cons` x))
|
||||
Just v2 -> Just (combine v1 v2, (NoPercent, ws))
|
||||
w0 = c2w '0'
|
||||
w9 = c2w '9'
|
||||
wa = c2w 'a'
|
||||
wf = c2w 'f'
|
||||
wA = c2w 'A'
|
||||
wF = c2w 'F'
|
||||
hexVal w
|
||||
| w0 <= w && w <= w9 = Just $ w - w0
|
||||
| wa <= w && w <= wf = Just $ w - wa + 10
|
||||
| wA <= w && w <= wF = Just $ w - wA + 10
|
||||
| otherwise = Nothing
|
||||
combine :: Word8 -> Word8 -> Word8
|
||||
combine a b = shiftL a 4 .|. b
|
||||
|
||||
data DecodeHelper = NoPercent | NoChar | OneChar (Word8, Word8)
|
30
runtests.hs
Normal file
30
runtests.hs
Normal file
@ -0,0 +1,30 @@
|
||||
import Test.Framework (defaultMain, testGroup, Test)
|
||||
import Test.Framework.Providers.HUnit
|
||||
import Test.HUnit hiding (Test)
|
||||
|
||||
import Network.Wai.Parse
|
||||
import qualified Data.ByteString.Char8 as B8
|
||||
import Control.Arrow
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain [testSuite]
|
||||
|
||||
testSuite :: Test
|
||||
testSuite = testGroup "Network.Wai.Parse"
|
||||
[ testCase "parseQueryString" caseParseQueryString
|
||||
]
|
||||
|
||||
caseParseQueryString :: Assertion
|
||||
caseParseQueryString = do
|
||||
let go l r =
|
||||
map (B8.pack *** B8.pack) l @=? parseQueryString (B8.pack r)
|
||||
|
||||
go [] ""
|
||||
go [("foo", "")] "foo"
|
||||
go [("foo", "bar")] "foo=bar"
|
||||
go [("foo", "bar"), ("baz", "bin")] "foo=bar&baz=bin"
|
||||
go [("%Q", "")] "%Q"
|
||||
go [("%1Q", "")] "%1Q"
|
||||
go [("%1", "")] "%1"
|
||||
go [("/", "")] "%2F"
|
||||
go [("/", "")] "%2f"
|
@ -1,5 +1,5 @@
|
||||
Name: wai-extra
|
||||
Version: 0.1.1.1
|
||||
Version: 0.1.2
|
||||
Synopsis: Provides some basic WAI handlers and middleware.
|
||||
Description: The goal here is to provide common features without many dependencies.
|
||||
License: BSD3
|
||||
@ -26,6 +26,7 @@ Library
|
||||
Network.Wai.Middleware.Gzip
|
||||
Network.Wai.Middleware.Jsonp
|
||||
Network.Wai.Zlib
|
||||
Network.Wai.Parse
|
||||
Other-modules: Network.Wai.Handler.Helper
|
||||
ghc-options: -Wall
|
||||
c-sources: c/helper.c
|
||||
|
Loading…
Reference in New Issue
Block a user