aboutsummaryrefslogtreecommitdiff
path: root/ch16
diff options
context:
space:
mode:
Diffstat (limited to 'ch16')
-rw-r--r--ch16/16_a_4.hs95
1 files changed, 95 insertions, 0 deletions
diff --git a/ch16/16_a_4.hs b/ch16/16_a_4.hs
new file mode 100644
index 0000000..c828c37
--- /dev/null
+++ b/ch16/16_a_4.hs
@@ -0,0 +1,95 @@
+-- Another popular attack is to open a connection and either leave it idle or
+-- send data extremely slowly.
+-- Write a wrapper in the IO monad that will invoke the parser. Use the
+-- System.Timeout module to close the connection if the parser does not complete
+-- within 30 seconds.
+
+-- The chapter doesn't use network connections in the parsing examples. Closing
+-- the connection probably means stopping the parse so it doesn't block for a
+-- long time.
+
+
+import Control.Concurrent (threadDelay)
+import System.Timeout (timeout)
+import Data.List (intercalate)
+
+
+{-- From examples/examples/ch16/HttpRequestParser.hs --}
+import Control.Applicative
+import Text.ParserCombinators.Parsec hiding (many, optional, (<|>))
+import Control.Monad (liftM4)
+
+p_headers :: CharParser st [(String, String)]
+p_headers = header `manyTill` crlf
+ where header = liftA2 (,) fieldName (char ':' *> spaces *> contents)
+ contents = liftA2 (++) (many1 notEOL <* crlf)
+ (continuation <|> pure [])
+ continuation = liftA2 (:) (' ' <$ many1 (oneOf " \t")) contents
+ fieldName = (:) <$> letter <*> many fieldChar
+ fieldChar = letter <|> digit <|> oneOf "-_"
+
+crlf :: CharParser st ()
+crlf = (() <$ string "\r\n") <|> (() <$ newline)
+
+notEOL :: CharParser st Char
+notEOL = noneOf "\r\n"
+
+data Method = Get | Post
+ deriving (Eq, Ord, Show)
+
+data HttpRequest = HttpRequest {
+ reqMethod :: Method
+ , reqURL :: String
+ , reqHeaders :: [(String, String)]
+ , reqBody :: Maybe String
+ } deriving (Eq, Show)
+
+p_request :: CharParser () HttpRequest
+p_request = q "GET" Get (pure Nothing)
+ <|> q "POST" Post (Just <$> many anyChar)
+ where q name ctor body = liftM4 HttpRequest req url p_headers body
+ where req = ctor <$ string name <* char ' '
+ url = optional (char '/') *>
+ manyTill notEOL (try $ string " HTTP/1." <* oneOf "01")
+ <* crlf
+{-- End of code from examples --}
+
+
+parseWithTimeout parser request = timeout timeoutMicrosec parse
+ where
+ timeoutMicrosec = 30 * 10^6 -- 30 seconds
+ parse = do
+ req <- request
+ let res = runParser p_request () "" req
+ return res
+
+
+
+
+testRequestWithDelay :: Int -> IO String
+testRequestWithDelay delayMicrosec = do
+ let start = intercalate "\r\n" [
+ "POST /index.html HTTP/1.1",
+ "Host: book.realworldhaskell.org",
+ "User-Agent: Mozilla/5.0",
+ "Accept: text/html",
+ "",
+ "Hello "
+ ]
+ threadDelay delayMicrosec
+ return (start ++ "World!")
+
+testRequestFast = testRequestWithDelay (25 * 10^6)
+
+testRequestSlow = testRequestWithDelay (32 * 10^6)
+
+
+-- ghci> :l 16_a_4.hs
+-- [1 of 2] Compiling Main ( 16_a_4.hs, interpreted )
+-- Ok, one module loaded.
+
+-- ghci> parseWithTimeout p_request testRequestFast
+-- Just (Right (HttpRequest {reqMethod = Post, reqURL = "index.html", reqHeaders = [("Host","book.realworldhaskell.org"),("User-Agent","Mozilla/5.0"),("Accept","text/html")], reqBody = Just "Hello World!"}))
+
+-- ghci> parseWithTimeout p_request testRequestSlow
+-- Nothing