PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/Parsec_RFC2616.hs

http://github.com/bos/attoparsec
Haskell | 88 lines | 71 code | 15 blank | 2 comment | 8 complexity | 8fe619d515f7853f9411d3da4f31737a MD5 | raw file
Possible License(s): BSD-3-Clause
  1. {-# LANGUAGE BangPatterns, CPP, FlexibleContexts #-}
  2. module Main (main) where
  3. import Control.Applicative
  4. import Control.Exception (bracket)
  5. import System.Environment (getArgs)
  6. import System.IO (hClose, openFile, IOMode(ReadMode))
  7. import Text.Parsec.Char (anyChar, char, satisfy, string)
  8. import Text.Parsec.Combinator (many1, manyTill, skipMany1)
  9. import Text.Parsec.Prim hiding (many, token, (<|>))
  10. import qualified Data.IntSet as S
  11. #if 1
  12. import Text.Parsec.ByteString.Lazy (Parser, parseFromFile)
  13. import qualified Data.ByteString.Lazy as B
  14. #else
  15. import Text.Parsec.ByteString (Parser, parseFromFile)
  16. import qualified Data.ByteString as B
  17. #endif
  18. token :: Stream s m Char => ParsecT s u m Char
  19. token = satisfy $ \c -> S.notMember (fromEnum c) set
  20. where set = S.fromList . map fromEnum $ ['\0'..'\31'] ++ "()<>@,;:\\\"/[]?={} \t" ++ ['\128'..'\255']
  21. isHorizontalSpace :: Char -> Bool
  22. isHorizontalSpace c = c == ' ' || c == '\t'
  23. skipHSpaces :: Stream s m Char => ParsecT s u m ()
  24. skipHSpaces = skipMany1 (satisfy isHorizontalSpace)
  25. data Request = Request {
  26. _requestMethod :: String
  27. , _requestUri :: String
  28. , _requestProtocol :: String
  29. } deriving (Eq, Ord, Show)
  30. requestLine :: Stream s m Char => ParsecT s u m Request
  31. requestLine = do
  32. method <- many1 token <* skipHSpaces
  33. uri <- many1 (satisfy (not . isHorizontalSpace)) <* skipHSpaces <* string "HTTP/"
  34. proto <- many httpVersion <* endOfLine
  35. return $! Request method uri proto
  36. where
  37. httpVersion = satisfy $ \c -> c == '1' || c == '0' || c == '.'
  38. endOfLine :: Stream s m Char => ParsecT s u m ()
  39. endOfLine = (string "\r\n" *> pure ()) <|> (char '\n' *> pure ())
  40. data Header = Header {
  41. _headerName :: String
  42. , _headerValue :: [String]
  43. } deriving (Eq, Ord, Show)
  44. messageHeader :: Stream s m Char => ParsecT s u m Header
  45. messageHeader = do
  46. header <- many1 token <* char ':' <* skipHSpaces
  47. body <- manyTill anyChar endOfLine
  48. conts <- many $ skipHSpaces *> manyTill anyChar endOfLine
  49. return $! Header header (body:conts)
  50. request :: Stream s m Char => ParsecT s u m (Request, [Header])
  51. request = (,) <$> requestLine <*> many messageHeader <* endOfLine
  52. listy :: FilePath -> IO ()
  53. listy arg = do
  54. r <- parseFromFile (many request) arg
  55. case r of
  56. Left err -> putStrLn $ arg ++ ": " ++ show err
  57. Right rs -> print (length rs)
  58. chunky :: FilePath -> IO ()
  59. chunky arg = bracket (openFile arg ReadMode) hClose $ \h ->
  60. loop (0::Int) =<< B.hGetContents h
  61. where
  62. loop !n bs
  63. | B.null bs = print n
  64. | otherwise = case parse myReq arg bs of
  65. Left err -> putStrLn $ arg ++ ": " ++ show err
  66. Right (r,bs') -> loop (n+1) bs'
  67. myReq :: Parser ((Request, [Header]), B.ByteString)
  68. myReq = liftA2 (,) request getInput
  69. main :: IO ()
  70. main = mapM_ f =<< getArgs
  71. where
  72. --f = listy
  73. f = chunky