diff --git a/test/WaiExtraTest.hs b/test/WaiExtraTest.hs
new file mode 100644
--- /dev/null
+++ b/test/WaiExtraTest.hs
@@ -0,0 +1,427 @@
+{-# LANGUAGE OverloadedStrings #-}
+module WaiExtraTest (specs) where
+
+import Test.Hspec.Monadic
+import Test.Hspec.HUnit ()
+import Test.HUnit hiding (Test)
+
+import Network.Wai
+import Network.Wai.Test
+import Network.Wai.Parse
+import qualified Data.ByteString as S
+import qualified Data.ByteString.Char8 as S8
+import qualified Data.ByteString.Lazy.Char8 as L8
+import qualified Data.ByteString.Lazy as L
+import qualified Data.Text.Lazy as T
+import Control.Arrow
+
+import Network.Wai.Middleware.Jsonp
+import Network.Wai.Middleware.Gzip
+import Network.Wai.Middleware.Vhost
+import Network.Wai.Middleware.Autohead
+import Network.Wai.Middleware.MethodOverride
+import Network.Wai.Middleware.AcceptOverride
+import Network.Wai.Middleware.Debug (debugHandle)
+import Codec.Compression.GZip (decompress)
+
+import Data.Enumerator (run_, enumList, ($$), Iteratee)
+import Data.Enumerator.Binary (enumFile)
+import Control.Monad.IO.Class (liftIO)
+import Data.Maybe (fromMaybe)
+import Network.HTTP.Types (parseSimpleQuery, status200)
+
+specs :: Specs
+specs = do
+  describe "Network.Wai.Parse" $ do
+    it "parseQueryString" caseParseQueryString
+    it "parseQueryString with question mark" caseParseQueryStringQM
+    it "parseHttpAccept" caseParseHttpAccept
+    it "parseRequestBody" caseParseRequestBody
+    {-
+    , it "findBound" caseFindBound
+    , it "sinkTillBound" caseSinkTillBound
+    , it "killCR" caseKillCR
+    , it "killCRLF" caseKillCRLF
+    , it "takeLine" caseTakeLine
+    -}
+    it "jsonp" caseJsonp
+    it "gzip" caseGzip
+    it "gzip not for MSIE" caseGzipMSIE
+    it "vhost" caseVhost
+    it "autohead" caseAutohead
+    it "method override" caseMethodOverride
+    it "accept override" caseAcceptOverride
+    it "dalvik multipart" caseDalvikMultipart
+    it "debug request body" caseDebugRequestBody
+
+caseParseQueryString :: Assertion
+caseParseQueryString = do
+    let go l r =
+            map (S8.pack *** S8.pack) l @=? parseSimpleQuery (S8.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"
+    go [("foo bar", "")] "foo+bar"
+
+caseParseQueryStringQM :: Assertion
+caseParseQueryStringQM = do
+    let go l r =
+            map (S8.pack *** S8.pack) l
+                @=? parseSimpleQuery (S8.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"
+    go [("foo bar", "")] "foo+bar"
+
+caseParseHttpAccept :: Assertion
+caseParseHttpAccept = do
+    let input = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"
+        expected = ["text/html", "text/x-c", "text/x-dvi", "text/plain"]
+    expected @=? parseHttpAccept input
+
+parseRequestBody' :: Sink ([S8.ByteString] -> [S8.ByteString]) L.ByteString
+                  -> SRequest
+                  -> Iteratee S.ByteString IO ([(S.ByteString, S.ByteString)], [(S.ByteString, FileInfo L.ByteString)])
+parseRequestBody' sink (SRequest req bod) =
+    enumList 1 (L.toChunks bod) $$ parseRequestBody sink req
+
+caseParseRequestBody :: Assertion
+caseParseRequestBody = run_ t where
+    content2 = S8.pack $
+        "--AaB03x\n" ++
+        "Content-Disposition: form-data; name=\"document\"; filename=\"b.txt\"\n" ++
+        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
+        "This is a file.\n" ++
+        "It has two lines.\n" ++
+        "--AaB03x\n" ++
+        "Content-Disposition: form-data; name=\"title\"\n" ++
+        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
+        "A File\n" ++
+        "--AaB03x\n" ++
+        "Content-Disposition: form-data; name=\"summary\"\n" ++
+        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
+        "This is my file\n" ++
+        "file test\n" ++
+        "--AaB03x--"
+    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"
+    t = do
+        let content1 = "foo=bar&baz=bin"
+        let ctype1 = "application/x-www-form-urlencoded"
+        result1 <- parseRequestBody' lbsSink $ toRequest ctype1 content1
+        liftIO $ assertEqual "parsing post x-www-form-urlencoded"
+                    (map (S8.pack *** S8.pack) [("foo", "bar"), ("baz", "bin")], [])
+                    result1
+
+        let ctype2 = "multipart/form-data; boundary=AaB03x"
+        result2 <- parseRequestBody' lbsSink $ toRequest ctype2 content2
+        let expectedsmap2 =
+              [ ("title", "A File")
+              , ("summary", "This is my file\nfile test")
+              ]
+        let textPlain = S8.pack $ "text/plain; charset=iso-8859-1"
+        let expectedfile2 =
+              [(S8.pack "document", FileInfo (S8.pack "b.txt") textPlain $ L8.pack
+                 "This is a file.\nIt has two lines.")]
+        let expected2 = (map (S8.pack *** S8.pack) expectedsmap2, expectedfile2)
+        liftIO $ assertEqual "parsing post multipart/form-data"
+                    expected2
+                    result2
+
+        let ctype3 = "multipart/form-data; boundary=----WebKitFormBoundaryB1pWXPZ6lNr8RiLh"
+        result3 <- parseRequestBody' lbsSink $ toRequest ctype3 content3
+        let expectedsmap3 = []
+        let expectedfile3 = [(S8.pack "yaml", FileInfo (S8.pack "README") (S8.pack "application/octet-stream") $
+                                L8.pack "Photo blog using Hack.\n")]
+        let expected3 = (expectedsmap3, expectedfile3)
+        liftIO $ assertEqual "parsing actual post multipart/form-data"
+                    expected3
+                    result3
+
+        result2' <- parseRequestBody' lbsSink $ toRequest' ctype2 content2
+        liftIO $ assertEqual "parsing post multipart/form-data 2"
+                    expected2
+                    result2'
+
+        result3' <- parseRequestBody' lbsSink $ toRequest' ctype3 content3
+        liftIO $ assertEqual "parsing actual post multipart/form-data 2"
+                    expected3
+                    result3'
+
+toRequest :: S8.ByteString -> S8.ByteString -> SRequest
+toRequest ctype content = SRequest defaultRequest
+    { requestHeaders = [("Content-Type", ctype)]
+    , requestMethod = "POST"
+    , rawPathInfo = ""
+    , rawQueryString = ""
+    , queryString = []
+    } (L.fromChunks [content])
+
+toRequest' :: S8.ByteString -> S8.ByteString -> SRequest
+toRequest' ctype content = SRequest defaultRequest
+    { requestHeaders = [("Content-Type", ctype)]
+    } (L.fromChunks $ map S.singleton $ S.unpack content)
+
+{-
+caseFindBound :: Assertion
+caseFindBound = do
+    findBound (S8.pack "def") (S8.pack "abcdefghi") @?=
+        FoundBound (S8.pack "abc") (S8.pack "ghi")
+    findBound (S8.pack "def") (S8.pack "ABC") @?= NoBound
+    findBound (S8.pack "def") (S8.pack "abcd") @?= PartialBound
+    findBound (S8.pack "def") (S8.pack "abcdE") @?= NoBound
+    findBound (S8.pack "def") (S8.pack "abcdEdef") @?=
+        FoundBound (S8.pack "abcdE") (S8.pack "")
+
+caseSinkTillBound :: Assertion
+caseSinkTillBound = do
+    let iter () _ = return ()
+    let src = S8.pack "this is some text"
+        bound1 = S8.pack "some"
+        bound2 = S8.pack "some!"
+    let enum = enumList 1 [src]
+    let helper _ _ = return ()
+    (_, res1) <- run_ $ enum $$ sinkTillBound bound1 helper ()
+    res1 @?= True
+    (_, res2) <- run_ $ enum $$ sinkTillBound bound2 helper ()
+    res2 @?= False
+
+caseKillCR :: Assertion
+caseKillCR = do
+    "foo" @=? killCR "foo"
+    "foo" @=? killCR "foo\r"
+    "foo\r\n" @=? killCR "foo\r\n"
+    "foo\r'" @=? killCR "foo\r'"
+
+caseKillCRLF :: Assertion
+caseKillCRLF = do
+    "foo" @=? killCRLF "foo"
+    "foo\r" @=? killCRLF "foo\r"
+    "foo" @=? killCRLF "foo\r\n"
+    "foo\r'" @=? killCRLF "foo\r'"
+    "foo" @=? killCRLF "foo\n"
+
+caseTakeLine :: Assertion
+caseTakeLine = do
+    helper "foo\nbar\nbaz" "foo"
+    helper "foo\r\nbar\nbaz" "foo"
+    helper "foo\nbar\r\nbaz" "foo"
+    helper "foo\rbar\r\nbaz" "foo\rbar"
+  where
+    helper haystack needle = do
+        x <- run_ $ enumList 1 [haystack] $$ takeLine
+        Just needle @=? x
+-}
+
+jsonpApp :: Application
+jsonpApp = jsonp $ const $ return $ responseLBS
+    status200
+    [("Content-Type", "application/json")]
+    "{\"foo\":\"bar\"}"
+
+caseJsonp :: Assertion
+caseJsonp = flip runSession jsonpApp $ do
+    sres1 <- request defaultRequest
+                { queryString = [("callback", Just "test")]
+                , requestHeaders = [("Accept", "text/javascript")]
+                }
+    assertContentType "text/javascript" sres1
+    assertBody "test({\"foo\":\"bar\"})" sres1
+
+    sres2 <- request defaultRequest
+                { queryString = [("call_back", Just "test")]
+                , requestHeaders = [("Accept", "text/javascript")]
+                }
+    assertContentType "application/json" sres2
+    assertBody "{\"foo\":\"bar\"}" sres2
+
+    sres3 <- request defaultRequest
+                { queryString = [("callback", Just "test")]
+                , requestHeaders = [("Accept", "text/html")]
+                }
+    assertContentType "application/json" sres3
+    assertBody "{\"foo\":\"bar\"}" sres3
+
+gzipApp :: Application
+gzipApp = gzip True $ const $ return $ responseLBS status200
+    [("Content-Type", "text/plain")]
+    "test"
+
+caseGzip :: Assertion
+caseGzip = flip runSession gzipApp $ do
+    sres1 <- request defaultRequest
+                { requestHeaders = [("Accept-Encoding", "gzip")]
+                }
+    assertHeader "Content-Encoding" "gzip" sres1
+    liftIO $ decompress (simpleBody sres1) @?= "test"
+
+    sres2 <- request defaultRequest
+                { requestHeaders = []
+                }
+    assertNoHeader "Content-Encoding" sres2
+    assertBody "test" sres2
+
+caseGzipMSIE :: Assertion
+caseGzipMSIE = flip runSession gzipApp $ do
+    sres1 <- request defaultRequest
+                { requestHeaders =
+                    [ ("Accept-Encoding", "gzip")
+                    , ("User-Agent", "Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 6.0)")
+                    ]
+                }
+    assertNoHeader "Content-Encoding" sres1
+    liftIO $ simpleBody sres1 @?= "test"
+
+vhostApp1, vhostApp2, vhostApp :: Application
+vhostApp1 = const $ return $ responseLBS status200 [] "app1"
+vhostApp2 = const $ return $ responseLBS status200 [] "app2"
+vhostApp = vhost
+    [ ((== "foo.com") . serverName, vhostApp1)
+    ]
+    vhostApp2
+
+caseVhost :: Assertion
+caseVhost = flip runSession vhostApp $ do
+    sres1 <- request defaultRequest
+                { serverName = "foo.com"
+                }
+    assertBody "app1" sres1
+
+    sres2 <- request defaultRequest
+                { serverName = "bar.com"
+                }
+    assertBody "app2" sres2
+
+autoheadApp :: Application
+autoheadApp = autohead $ const $ return $ responseLBS status200
+    [("Foo", "Bar")] "body"
+
+caseAutohead :: Assertion
+caseAutohead = flip runSession autoheadApp $ do
+    sres1 <- request defaultRequest
+                { requestMethod = "GET"
+                }
+    assertHeader "Foo" "Bar" sres1
+    assertBody "body" sres1
+
+    sres2 <- request defaultRequest
+                { requestMethod = "HEAD"
+                }
+    assertHeader "Foo" "Bar" sres2
+    assertBody "" sres2
+
+moApp :: Application
+moApp = methodOverride $ \req -> return $ responseLBS status200
+    [("Method", requestMethod req)] ""
+
+caseMethodOverride :: Assertion
+caseMethodOverride = flip runSession moApp $ do
+    sres1 <- request defaultRequest
+                { requestMethod = "GET"
+                , queryString = []
+                }
+    assertHeader "Method" "GET" sres1
+
+    sres2 <- request defaultRequest
+                { requestMethod = "POST"
+                , queryString = []
+                }
+    assertHeader "Method" "POST" sres2
+
+    sres3 <- request defaultRequest
+                { requestMethod = "POST"
+                , queryString = [("_method", Just "PUT")]
+                }
+    assertHeader "Method" "PUT" sres3
+
+aoApp :: Application
+aoApp = acceptOverride $ \req -> return $ responseLBS status200
+    [("Accept", fromMaybe "" $ lookup "Accept" $ requestHeaders req)] ""
+
+caseAcceptOverride :: Assertion
+caseAcceptOverride = flip runSession aoApp $ do
+    sres1 <- request defaultRequest
+                { queryString = []
+                , requestHeaders = [("Accept", "foo")]
+                }
+    assertHeader "Accept" "foo" sres1
+
+    sres2 <- request defaultRequest
+                { queryString = []
+                , requestHeaders = [("Accept", "bar")]
+                }
+    assertHeader "Accept" "bar" sres2
+
+    sres3 <- request defaultRequest
+                { queryString = [("_accept", Just "baz")]
+                , requestHeaders = [("Accept", "bar")]
+                }
+    assertHeader "Accept" "baz" sres3
+
+caseDalvikMultipart :: Assertion
+caseDalvikMultipart = do
+    let headers =
+            [ ("content-length", "12098")
+            , ("content-type", "multipart/form-data;boundary=*****")
+            , ("GATEWAY_INTERFACE", "CGI/1.1")
+            , ("PATH_INFO", "/")
+            , ("QUERY_STRING", "")
+            , ("REMOTE_ADDR", "192.168.1.115")
+            , ("REMOTE_HOST", "ganjizza")
+            , ("REQUEST_URI", "http://192.168.1.115:3000/")
+            , ("REQUEST_METHOD", "POST")
+            , ("HTTP_CONNECTION", "Keep-Alive")
+            , ("HTTP_COOKIE", "_SESSION=fgUGM5J/k6mGAAW+MMXIJZCJHobw/oEbb6T17KQN0p9yNqiXn/m/ACrsnRjiCEgqtG4fogMUDI+jikoFGcwmPjvuD5d+MDz32iXvDdDJsFdsFMfivuey2H+n6IF6yFGD")
+            , ("HTTP_USER_AGENT", "Dalvik/1.1.0 (Linux; U; Android 2.1-update1; sdk Build/ECLAIR)")
+            , ("HTTP_HOST", "192.168.1.115:3000")
+            , ("HTTP_ACCEPT", "*, */*")
+            , ("HTTP_VERSION", "HTTP/1.1")
+            , ("REQUEST_PATH", "/")
+            ]
+    let request' = defaultRequest
+            { requestHeaders = headers
+            }
+    (params, files) <- run_ $ enumFile "test/requests/dalvik-request" $$ parseRequestBody lbsSink request'
+    lookup "scannedTime" params @?= Just "1.298590056748E9"
+    lookup "geoLong" params @?= Just "0"
+    lookup "geoLat" params @?= Just "0"
+    length files @?= 1
+
+caseDebugRequestBody :: Assertion
+caseDebugRequestBody = do
+    flip runSession (debugApp postOutput) $ do
+        let req = toRequest "application/x-www-form-urlencoded" "foo=bar&baz=bin"
+        res <- srequest req
+        assertStatus 200 res
+
+    let qs = "?foo=bar&baz=bin"
+    flip runSession (debugApp $ getOutput qs) $ do
+        assertStatus 200 =<< request defaultRequest
+                { requestMethod = "GET"
+                , queryString = map (\(k,v) -> (k, Just v)) params
+                , rawQueryString = qs
+                , requestHeaders = []
+                , rawPathInfo = "/location"
+                }
+  where
+    params = [("foo", "bar"), ("baz", "bin")]
+    postOutput = T.pack $ "POST \nAccept: \nPOST " ++ (show params)
+    getOutput _qs = T.pack $ "GET /location" ++ "\nAccept: \nGET " ++ (show params) -- \nAccept: \n" ++ (show params)
+
+    debugApp output = debugHandle (\t -> liftIO $ assertEqual "debug" output t) $ \_req -> do
+        return $ responseLBS status200 [ ] ""
+    {-debugApp = debug $ \req -> do-}
+        {-return $ responseLBS status200 [ ] ""-}
diff --git a/wai-extra.cabal b/wai-extra.cabal
--- a/wai-extra.cabal
+++ b/wai-extra.cabal
@@ -1,5 +1,5 @@
 Name:                wai-extra
-Version:             0.4.5.1
+Version:             0.4.5.2
 Synopsis:            Provides some basic WAI handlers and middleware.
 Description:         The goal here is to provide common features without many dependencies.
 License:             BSD3
@@ -17,6 +17,7 @@
   test/json
   test/test.html
   test/sample.hs
+  test/WaiExtraTest.hs
 
 Library
   Build-Depends:     base                      >= 4 && < 5
