This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "snap-server".

The branch, master has been updated
       via  2722cbf1584150dbc99ba692ccfa933be4a1d909 (commit)
      from  69705ae80e80fff2100e9645aa7013bbe4f50b3e (commit)


Summary of changes:
 snap-server.cabal                             |    2 +-
 src/Snap/Internal/Http/Server.hs              |   70 ++++++++++++++++++------
 test/suite/Snap/Internal/Http/Server/Tests.hs |   57 ++++++++++++++++++--
 3 files changed, 105 insertions(+), 24 deletions(-)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 2722cbf1584150dbc99ba692ccfa933be4a1d909
Author: Gregory Collins <[email protected]>
Date:   Sun Mar 20 16:44:11 2011 +0100

    Send 411 when PUT/POST requests come in with no content-length

diff --git a/snap-server.cabal b/snap-server.cabal
index 21dae36..f04e383 100644
--- a/snap-server.cabal
+++ b/snap-server.cabal
@@ -118,7 +118,7 @@ Library
     murmur-hash >= 0.1 && < 0.2,
     network >= 2.3 && <2.4,
     old-locale,
-    snap-core >= 0.4.1 && <0.5,
+    snap-core >= 0.4.2 && <0.5,
     template-haskell,
     time,
     transformers,
diff --git a/src/Snap/Internal/Http/Server.hs b/src/Snap/Internal/Http/Server.hs
index 49f0d76..66ec994 100644
--- a/src/Snap/Internal/Http/Server.hs
+++ b/src/Snap/Internal/Http/Server.hs
@@ -1,7 +1,8 @@
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE DeriveDataTypeable  #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE RankNTypes          #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Snap.Internal.Http.Server where
@@ -27,10 +28,11 @@ import           Data.IORef
 import           Data.List (foldl')
 import           Data.Map (Map)
 import qualified Data.Map as Map
-import           Data.Maybe (fromJust, catMaybes, fromMaybe)
+import           Data.Maybe (catMaybes, fromJust, fromMaybe)
 import           Data.Monoid
-import           Data.Version
 import           Data.Time
+import           Data.Typeable
+import           Data.Version
 import           GHC.Conc
 import           System.PosixCompat.Files hiding (setFileSize)
 import           System.Posix.Types (FileOffset)
@@ -88,6 +90,14 @@ data EventLoopType = EventLoopSimple
 
 
 ------------------------------------------------------------------------------
+-- This exception will be thrown if we decided to terminate the request before
+-- running the user handler.
+data TerminatedBeforeHandlerException = TerminatedBeforeHandlerException
+  deriving (Show, Typeable)
+instance Exception TerminatedBeforeHandlerException
+
+
+------------------------------------------------------------------------------
 defaultEvType :: EventLoopType
 #ifdef LIBEV
 defaultEvType = EventLoopLibEv
@@ -250,9 +260,10 @@ runHTTP :: Int                           -- ^ default 
timeout
         -> IO ()
 runHTTP defaultTimeout alog elog handler lh sinfo readEnd writeEnd onSendFile
         tickle =
-    go `catches` [ Handler $ \(e :: AsyncException) -> do
+    go `catches` [ Handler $ \(_ :: TerminatedBeforeHandlerException) -> do
+                       return ()
+                 , Handler $ \(e :: AsyncException) -> do
                        throwIO e
-
                  , Handler $ \(e :: SomeException) ->
                        logE elog $ S.concat [ logPrefix , bshow e ] ]
 
@@ -310,7 +321,7 @@ httpSession defaultTimeout writeEnd' buffer onSendFile 
tickle handler = do
     let writeEnd = iterateeDebugWrapper "writeEnd" writeEnd'
 
     liftIO $ debug "Server.httpSession: entered"
-    mreq  <- receiveRequest
+    mreq  <- receiveRequest writeEnd
     liftIO $ debug "Server.httpSession: receiveRequest finished"
 
     -- successfully got a request, so restart timer
@@ -408,8 +419,30 @@ checkExpect100Continue req writeEnd = do
 
 
 ------------------------------------------------------------------------------
-receiveRequest :: ServerMonad (Maybe Request)
-receiveRequest = do
+return411 :: Request
+          -> Iteratee ByteString IO ()
+          -> ServerMonad a
+return411 req writeEnd = do
+    go
+    liftIO $ throwIO $ TerminatedBeforeHandlerException
+
+  where
+    go = do
+        let (major,minor) = rqVersion req
+        let hl = mconcat [ fromByteString "HTTP/"
+                         , fromShow major
+                         , fromWord8 $ c2w '.'
+                         , fromShow minor
+                         , fromByteString " 411 Length Required\r\n\r\n"
+                         , fromByteString "411 Length Required\r\n" ]
+        liftIO $ runIteratee
+                   ((enumBS (toByteString hl) >==> enumEOF) $$ writeEnd)
+        return ()
+
+
+------------------------------------------------------------------------------
+receiveRequest :: Iteratee ByteString IO () -> ServerMonad (Maybe Request)
+receiveRequest writeEnd = do
     debug "receiveRequest: entered"
     mreq <- {-# SCC "receiveRequest/parseRequest" #-} lift $
             iterateeDebugWrapper "parseRequest" parseRequest
@@ -469,15 +502,17 @@ receiveRequest = do
                 joinI $ takeExactly len st'
 
         noContentLength :: Request -> ServerMonad ()
-        noContentLength rq = liftIO $ do
+        noContentLength rq = do
             debug ("receiveRequest/setEnumerator: " ++
                    "request did NOT have content-length")
+
+            when (rqMethod rq == POST || rqMethod rq == PUT) $
+                 return411 req writeEnd
+
             let enum = SomeEnumerator $
-                       if rqMethod rq == POST || rqMethod rq == PUT
-                         then returnI
-                         else iterateeDebugWrapper "noContentLength" .
-                              joinI . I.take 0
-            writeIORef (rqBody rq) enum
+                       iterateeDebugWrapper "noContentLength" .
+                       joinI . I.take 0
+            liftIO $ writeIORef (rqBody rq) enum
             debug "receiveRequest/setEnumerator: body enumerator set"
 
 
@@ -543,7 +578,6 @@ receiveRequest = do
             -- will override in "setEnumerator"
             enum <- liftIO $ newIORef $ SomeEnumerator (enumBS "")
 
-
             return $ Request serverName
                              serverPort
                              remoteAddr
diff --git a/test/suite/Snap/Internal/Http/Server/Tests.hs 
b/test/suite/Snap/Internal/Http/Server/Tests.hs
index a855b5a..43ab0db 100644
--- a/test/suite/Snap/Internal/Http/Server/Tests.hs
+++ b/test/suite/Snap/Internal/Http/Server/Tests.hs
@@ -71,6 +71,7 @@ tests = [ testHttpRequest1
         , testHttp1
         , testHttp2
         , testHttp100
+        , test411
         , testExpectGarbage
         , testPartialParse
         , testMethodParsing
@@ -112,6 +113,14 @@ sampleRequestExpectContinue =
              , "\r\n"
              , "0123456789" ]
 
+sampleRequest411 :: ByteString
+sampleRequest411 =
+    S.concat [ "\r\nPOST /foo/bar.html?param1=abc&param2=def%20+&param1=abc 
HTTP/1.1\r\n"
+             , "Host: www.zabble.com:7777\r\n"
+             , "X-Random-Other-Header: foo\r\n bar\r\n"
+             , "Cookie: foo=\"bar\\\"\"\r\n"
+             , "\r\n" ]
+
 sampleRequestExpectGarbage :: ByteString
 sampleRequestExpectGarbage =
     S.concat [ "\r\nGET /foo/bar.html?param1=abc&param2=def%20+&param1=abc 
HTTP/1.1\r\n"
@@ -140,17 +149,20 @@ testMethodParsing =
     ms = [ GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT ]
 
 
+dummyIter :: Iteratee ByteString IO ()
+dummyIter = consume >> return ()
+
 
 mkRequest :: ByteString -> IO Request
 mkRequest s = do
-    step <- runIteratee $ liftM fromJust $ rsm receiveRequest
+    step <- runIteratee $ liftM fromJust $ rsm $ receiveRequest dummyIter
     let iter = enumBS s step
     run_ iter
 
 
 testReceiveRequest :: Iteratee ByteString IO (Request,L.ByteString)
 testReceiveRequest = do
-    r  <- liftM fromJust $ rsm receiveRequest
+    r  <- liftM fromJust $ rsm $ receiveRequest dummyIter
     se <- liftIO $ readIORef (rqBody r)
     let (SomeEnumerator e) = se
     it  <- liftM e $ lift $ runIteratee copyingStream2Stream
@@ -230,7 +242,7 @@ testMultiRequest =
 
 testOneMethod :: Method -> IO ()
 testOneMethod m = do
-    step    <- runIteratee $ liftM fromJust $ rsm receiveRequest
+    step    <- runIteratee $ liftM fromJust $ rsm $ receiveRequest dummyIter
     let iter = enumLBS txt step
     req     <- run_ iter
 
@@ -253,7 +265,7 @@ expectException m = do
 
 testPartialParse :: Test
 testPartialParse = testCase "server/short" $ do
-    step <- runIteratee $ liftM fromJust $ rsm receiveRequest
+    step <- runIteratee $ liftM fromJust $ rsm $ receiveRequest dummyIter
     let iter = enumBS sampleShortRequest step
 
     expectException $ run_ iter
@@ -261,7 +273,7 @@ testPartialParse = testCase "server/short" $ do
 
 methodTestText :: Method -> L.ByteString
 methodTestText m = L.concat [ (L.pack $ map c2w $ show m)
-                        , " / HTTP/1.1\r\n\r\n" ]
+                        , " / HTTP/1.1\r\nContent-Length: 0\r\n\r\n" ]
 
 
 sampleRequest2 :: ByteString
@@ -727,6 +739,41 @@ testHttp100 = testCase "server/expect100" $ do
     assertBool "100 Continue" ok
 
 
+test411 :: Test
+test411 = testCase "server/expect411" $ do
+    let enumBody = enumBS sampleRequest411
+
+    ref <- newIORef ""
+
+    let (iter,onSendFile) = mkIter ref
+
+    runHTTP 60
+            Nothing
+            Nothing
+            echoServer2
+            "localhost"
+            (SessionInfo "127.0.0.1" 80 "127.0.0.1" 58384 False)
+            enumBody
+            iter
+            onSendFile
+            (const $ return ())
+
+    s <- readIORef ref
+
+    let lns = LC.lines s
+
+    let ok = case lns of
+               ("HTTP/1.1 411 Length Required\r":_) -> True
+               _ -> False
+
+    when (not ok) $ do
+        putStrLn "expect411 fail! got:"
+        LC.putStrLn s
+
+    assertBool "411 Length Required" ok
+
+
+
 testExpectGarbage :: Test
 testExpectGarbage = testCase "server/Expect: garbage" $ do
     let enumBody = enumBS sampleRequestExpectGarbage
-----------------------------------------------------------------------


hooks/post-receive
-- 
snap-server
_______________________________________________
Snap mailing list
[email protected]
http://mailman-mail5.webfaction.com/listinfo/snap

Reply via email to