@@ -0,0 +1,95 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE BangPatterns #-}
6+import Control.Exception (throwIO)
8+import qualified Data.ByteString as S
9+import Data.ByteString.Char8 (ByteString)
10+import qualified Data.ByteString.Char8 as B (unpack)
11+import qualified Network.HTTP.Types as H
12+import Network.Wai.Handler.Warp.Types
13+import Prelude hiding (lines)
18+-- >>> :set -XOverloadedStrings
22+ let requestLine1 = "GET http://www.example.com HTTP/1.1"
23+ let requestLine2 = "GET http://www.example.com/cgi-path/search.cgi?key=parser HTTP/1.0"
25+ bgroup "requestLine1" [
26+ bench "parseRequestLine1" $ parseRequestLine1 requestLine1
27+ , bench "parseRequestLine0" $ parseRequestLine0 requestLine1
29+ , bgroup "requestLine2" [
30+ bench "parseRequestLine1" $ parseRequestLine1 requestLine2
31+ , bench "parseRequestLine0" $ parseRequestLine0 requestLine2
35+----------------------------------------------------------------
37+parseRequestLine2 :: ByteString
42+parseRequestLine2 requestLine = undefined
44+----------------------------------------------------------------
48+-- >>> parseRequestLine1 "GET / HTTP/1.1"
49+-- ("GET","/","",HTTP/1.1)
50+-- >>> parseRequestLine1 "POST /cgi/search.cgi?key=foo HTTP/1.0"
51+-- ("POST","/cgi/search.cgi","?key=foo",HTTP/1.0)
52+-- >>> parseRequestLine1 "GET /NoHTTPVersion"
53+-- *** Exception: BadFirstLine "GET /NoHTTPVersion"
54+-- >>> parseRequestLine1 "GET /NotHTTP UNKNOWN/1.1"
55+-- *** Exception: NonHttp
56+parseRequestLine1 :: ByteString
61+parseRequestLine1 requestLine = do
62+ let (!method,!rest) = S.breakByte 32 requestLine -- ' '
63+ (!pathQuery,!httpVer') = S.breakByte 32 (S.drop 1 rest) -- ' '
64+ !httpVer = S.drop 1 httpVer'
65+ when (rest == "" || httpVer == "") $
66+ throwIO $ BadFirstLine $ B.unpack requestLine
67+ let (!path,!query) = S.breakByte 63 pathQuery -- '?'
68+ (!http,!ver) = S.breakByte 47 httpVer -- '/'
69+ when (http /= "HTTP") $ throwIO NonHttp
70+ let !hv | ver == "/1.1" = H.http11
71+ | otherwise = H.http10
72+ return $! (method,path,query,hv)
74+----------------------------------------------------------------
76+parseRequestLine0 :: ByteString
82+ case filter (not . S.null) $ S.splitWith (\c -> c == 32 || c == 9) s of -- '
83+ (method':query:http'') -> do
84+ let !method = method'
85+ !http' = S.concat http''
86+ (!hfirst, !hsecond) = S.splitAt 5 http'
88+ then let (!rpath, !qstring) = S.breakByte 63 query -- '?'
93+ in return $! (method, rpath, qstring, hv)
95+ _ -> throwIO $ BadFirstLine $ B.unpack s