1{-# LANGUAGE OverloadedStrings #-}
2import Test.Framework (defaultMain, testGroup, Test)
3import Test.Framework.Providers.HUnit
4import Test.HUnit hiding (Test)
8import Network.Wai.Parse
9import qualified Data.ByteString as S
10import qualified Data.ByteString.Char8 as S8
11import qualified Data.ByteString.Lazy.Char8 as L8
12import qualified Data.ByteString.Lazy as L
15import Network.Wai.Middleware.Jsonp
16import Network.Wai.Middleware.Gzip
17import Network.Wai.Middleware.Vhost
18import Network.Wai.Middleware.Autohead
19import Network.Wai.Middleware.MethodOverride
20import Network.Wai.Middleware.AcceptOverride
21import Codec.Compression.GZip (decompress)
23import Data.Enumerator (run_, enumList, ($$), Iteratee)
24import Data.Enumerator.Binary (enumFile)
25import Control.Monad.IO.Class (liftIO)
26import Data.Maybe (fromMaybe)
29main = defaultMain [testSuite]
32testSuite = testGroup "Network.Wai.Parse"
33 [ testCase "parseQueryString" caseParseQueryString
34 , testCase "parseQueryString with question mark" caseParseQueryStringQM
35 , testCase "parseHttpAccept" caseParseHttpAccept
36 , testCase "parseRequestBody" caseParseRequestBody
37 , testCase "findBound" caseFindBound
38 , testCase "sinkTillBound" caseSinkTillBound
39 , testCase "killCR" caseKillCR
40 , testCase "killCRLF" caseKillCRLF
41 , testCase "takeLine" caseTakeLine
42 , testCase "jsonp" caseJsonp
43 , testCase "gzip" caseGzip
44 , testCase "vhost" caseVhost
45 , testCase "autohead" caseAutohead
46 , testCase "method override" caseMethodOverride
47 , testCase "accept override" caseAcceptOverride
48 , testCase "dalvik multipart" caseDalvikMultipart
51caseParseQueryString :: Assertion
52caseParseQueryString = do
54 map (S8.pack *** S8.pack) l @=? parseQueryString (S8.pack r)
57 go [("foo", "")] "foo"
58 go [("foo", "bar")] "foo=bar"
59 go [("foo", "bar"), ("baz", "bin")] "foo=bar&baz=bin"
61 go [("%1Q", "")] "%1Q"
65 go [("foo bar", "")] "foo+bar"
67caseParseQueryStringQM :: Assertion
68caseParseQueryStringQM = do
70 map (S8.pack *** S8.pack) l
71 @=? parseQueryString (S8.pack $ '?' : r)
74 go [("foo", "")] "foo"
75 go [("foo", "bar")] "foo=bar"
76 go [("foo", "bar"), ("baz", "bin")] "foo=bar&baz=bin"
78 go [("%1Q", "")] "%1Q"
82 go [("foo bar", "")] "foo+bar"
84caseParseHttpAccept :: Assertion
85caseParseHttpAccept = do
87 S8.pack "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"
88 expected = ["text/html", "text/x-c", "text/x-dvi", "text/plain"]
89 map S8.pack expected @=? parseHttpAccept input
91parseRequestBody' :: Sink ([S8.ByteString] -> [S8.ByteString]) L.ByteString
93 -> Iteratee S.ByteString IO ([(S.ByteString, S.ByteString)], [(S.ByteString, FileInfo L.ByteString)])
94parseRequestBody' sink (SRequest req bod) =
95 enumList 1 (L.toChunks bod) $$ parseRequestBody sink req
97caseParseRequestBody :: Assertion
98caseParseRequestBody = run_ t where
101 "Content-Disposition: form-data; name=\"document\"; filename=\"b.txt\"\n" ++
102 "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
103 "This is a file.\n" ++
104 "It has two lines.\n" ++
106 "Content-Disposition: form-data; name=\"title\"\n" ++
107 "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
110 "Content-Disposition: form-data; name=\"summary\"\n" ++
111 "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
112 "This is my file\n" ++
115 content3 = S8.pack "------WebKitFormBoundaryB1pWXPZ6lNr8RiLh\r\nContent-Disposition: form-data; name=\"yaml\"; filename=\"README\"\r\nContent-Type: application/octet-stream\r\n\r\nPhoto blog using Hack.\n\r\n------WebKitFormBoundaryB1pWXPZ6lNr8RiLh--\r\n"
117 let content1 = S8.pack "foo=bar&baz=bin"
118 let ctype1 = S8.pack "application/x-www-form-urlencoded"
119 result1 <- parseRequestBody' lbsSink $ toRequest ctype1 content1
120 liftIO $ assertEqual "parsing post x-www-form-urlencoded"
121 (map (S8.pack *** S8.pack) [("foo", "bar"), ("baz", "bin")], [])
124 let ctype2 = S8.pack "multipart/form-data; boundary=AaB03x"
125 result2 <- parseRequestBody' lbsSink $ toRequest ctype2 content2
127 [ ("title", "A File")
128 , ("summary", "This is my file\nfile test")
130 let textPlain = S8.pack $ "text/plain; charset=iso-8859-1"
132 [(S8.pack "document", FileInfo (S8.pack "b.txt") textPlain $ L8.pack
133 "This is a file.\nIt has two lines.")]
134 let expected2 = (map (S8.pack *** S8.pack) expectedsmap2, expectedfile2)
135 liftIO $ assertEqual "parsing post multipart/form-data"
139 let ctype3 = S8.pack "multipart/form-data; boundary=----WebKitFormBoundaryB1pWXPZ6lNr8RiLh"
140 result3 <- parseRequestBody' lbsSink $ toRequest ctype3 content3
141 let expectedsmap3 = []
142 let expectedfile3 = [(S8.pack "yaml", FileInfo (S8.pack "README") (S8.pack "application/octet-stream") $
143 L8.pack "Photo blog using Hack.\n")]
144 let expected3 = (expectedsmap3, expectedfile3)
145 liftIO $ assertEqual "parsing actual post multipart/form-data"
149 result2' <- parseRequestBody' lbsSink $ toRequest' ctype2 content2
150 liftIO $ assertEqual "parsing post multipart/form-data 2"
154 result3' <- parseRequestBody' lbsSink $ toRequest' ctype3 content3
155 liftIO $ assertEqual "parsing actual post multipart/form-data 2"
159toRequest :: S8.ByteString -> S8.ByteString -> SRequest
160toRequest ctype content = SRequest (Request
161 { requestHeaders = [("Content-Type", ctype)]
162 }) (L.fromChunks [content])
164toRequest' :: S8.ByteString -> S8.ByteString -> SRequest
165toRequest' ctype content = SRequest (Request
166 { requestHeaders = [("Content-Type", ctype)]
167 }) (L.fromChunks $ map S.singleton $ S.unpack content)
169caseFindBound :: Assertion
171 findBound (S8.pack "def") (S8.pack "abcdefghi") @?=
172 FoundBound (S8.pack "abc") (S8.pack "ghi")
173 findBound (S8.pack "def") (S8.pack "ABC") @?= NoBound
174 findBound (S8.pack "def") (S8.pack "abcd") @?= PartialBound
175 findBound (S8.pack "def") (S8.pack "abcdE") @?= NoBound
176 findBound (S8.pack "def") (S8.pack "abcdEdef") @?=
177 FoundBound (S8.pack "abcdE") (S8.pack "")
179caseSinkTillBound :: Assertion
180caseSinkTillBound = do
181 let iter () _ = return ()
182 let src = S8.pack "this is some text"
183 bound1 = S8.pack "some"
184 bound2 = S8.pack "some!"
185 let enum = enumList 1 [src]
186 let helper _ _ = return ()
187 (_, res1) <- run_ $ enum $$ sinkTillBound bound1 helper ()
189 (_, res2) <- run_ $ enum $$ sinkTillBound bound2 helper ()
192caseKillCR :: Assertion
194 "foo" @=? killCR "foo"
195 "foo" @=? killCR "foo\r"
196 "foo\r\n" @=? killCR "foo\r\n"
197 "foo\r'" @=? killCR "foo\r'"
199caseKillCRLF :: Assertion
201 "foo" @=? killCRLF "foo"
202 "foo\r" @=? killCRLF "foo\r"
203 "foo" @=? killCRLF "foo\r\n"
204 "foo\r'" @=? killCRLF "foo\r'"
205 "foo" @=? killCRLF "foo\n"
207caseTakeLine :: Assertion
209 helper "foo\nbar\nbaz" "foo"
210 helper "foo\r\nbar\nbaz" "foo"
211 helper "foo\nbar\r\nbaz" "foo"
212 helper "foo\rbar\r\nbaz" "foo\rbar"
214 helper haystack needle = do
215 x <- run_ $ enumList 1 [haystack] $$ takeLine
218jsonpApp = jsonp $ const $ return $ responseLBS
220 [("Content-Type", "application/json")]
223caseJsonp :: Assertion
224caseJsonp = flip runSession jsonpApp $ do
225 sres1 <- request Request
226 { queryString = "callback=test"
227 , requestHeaders = [("Accept", "text/javascript")]
229 assertContentType "text/javascript" sres1
230 assertBody "test({\"foo\":\"bar\"})" sres1
232 sres2 <- request Request
233 { queryString = "call_back=test"
234 , requestHeaders = [("Accept", "text/javascript")]
236 assertContentType "application/json" sres2
237 assertBody "{\"foo\":\"bar\"}" sres2
239 sres3 <- request Request
240 { queryString = "callback=test"
241 , requestHeaders = [("Accept", "text/html")]
243 assertContentType "application/json" sres3
244 assertBody "{\"foo\":\"bar\"}" sres3
246gzipApp = gzip True $ const $ return $ responseLBS status200 [] "test"
249caseGzip = flip runSession gzipApp $ do
250 sres1 <- request Request
251 { requestHeaders = [("Accept-Encoding", "gzip")]
253 assertHeader "Content-Encoding" "gzip" sres1
254 liftIO $ decompress (simpleBody sres1) @?= "test"
256 sres2 <- request Request
257 { requestHeaders = []
259 assertNoHeader "Content-Encoding" sres2
260 assertBody "test" sres2
262vhostApp1 = const $ return $ responseLBS status200 [] "app1"
263vhostApp2 = const $ return $ responseLBS status200 [] "app2"
265 [ ((== "foo.com") . serverName, vhostApp1)
269caseVhost = flip runSession vhostApp $ do
270 sres1 <- request Request
271 { serverName = "foo.com"
273 assertBody "app1" sres1
275 sres2 <- request Request
276 { serverName = "bar.com"
278 assertBody "app2" sres2
280autoheadApp = autohead $ const $ return $ responseLBS status200
281 [("Foo", "Bar")] "body"
283caseAutohead = flip runSession autoheadApp $ do
284 sres1 <- request Request
285 { requestMethod = "GET"
287 assertHeader "Foo" "Bar" sres1
288 assertBody "body" sres1
290 sres1 <- request Request
291 { requestMethod = "HEAD"
293 assertHeader "Foo" "Bar" sres1
296moApp = methodOverride $ \req -> return $ responseLBS status200
297 [("Method", requestMethod req)] ""
299caseMethodOverride = flip runSession moApp $ do
300 sres1 <- request Request
301 { requestMethod = "GET"
304 assertHeader "Method" "GET" sres1
306 sres2 <- request Request
307 { requestMethod = "POST"
310 assertHeader "Method" "POST" sres2
312 sres3 <- request Request
313 { requestMethod = "POST"
314 , queryString = "_method=PUT"
316 assertHeader "Method" "PUT" sres3
318aoApp = acceptOverride $ \req -> return $ responseLBS status200
319 [("Accept", fromMaybe "" $ lookup "Accept" $ requestHeaders req)] ""
321caseAcceptOverride = flip runSession aoApp $ do
322 sres1 <- request Request
324 , requestHeaders = [("Accept", "foo")]
326 assertHeader "Accept" "foo" sres1
328 sres2 <- request Request
330 , requestHeaders = [("Accept", "bar")]
332 assertHeader "Accept" "bar" sres2
334 sres3 <- request Request
335 { queryString = "_accept=baz"
336 , requestHeaders = [("Accept", "bar")]
338 assertHeader "Accept" "baz" sres3
340caseDalvikMultipart = do
342 [ ("content-length", "12098")
343 , ("content-type", "multipart/form-data;boundary=*****")
344 , ("GATEWAY_INTERFACE", "CGI/1.1")
346 , ("QUERY_STRING", "")
347 , ("REMOTE_ADDR", "192.168.1.115")
348 , ("REMOTE_HOST", "ganjizza")
349 , ("REQUEST_URI", "http://192.168.1.115:3000/")
350 , ("REQUEST_METHOD", "POST")
351 , ("HTTP_CONNECTION", "Keep-Alive")
352 , ("HTTP_COOKIE", "_SESSION=fgUGM5J/k6mGAAW+MMXIJZCJHobw/oEbb6T17KQN0p9yNqiXn/m/ACrsnRjiCEgqtG4fogMUDI+jikoFGcwmPjvuD5d+MDz32iXvDdDJsFdsFMfivuey2H+n6IF6yFGD")
353 , ("HTTP_USER_AGENT", "Dalvik/1.1.0 (Linux; U; Android 2.1-update1; sdk Build/ECLAIR)")
354 , ("HTTP_HOST", "192.168.1.115:3000")
355 , ("HTTP_ACCEPT", "*, */*")
356 , ("HTTP_VERSION", "HTTP/1.1")
357 , ("REQUEST_PATH", "/")
359 let request = Request
360 { requestHeaders = headers
362 (params, files) <- run_ $ enumFile "test/dalvik-request" $$ parseRequestBody lbsSink request
363 lookup "scannedTime" params @?= Just "1.298590056748E9"
364 lookup "geoLong" params @?= Just "0"
365 lookup "geoLat" params @?= Just "0"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list