diff --git a/snap-core.cabal b/snap-core.cabal
--- a/snap-core.cabal
+++ b/snap-core.cabal
@@ -1,5 +1,5 @@
 name:           snap-core
-version:        0.5.1.4
+version:        0.5.2
 synopsis:       Snap: A Haskell Web Framework (Core)
 
 description:
@@ -138,7 +138,7 @@
     deepseq >= 1.1 && <1.2,
     directory,
     dlist >= 0.5 && < 0.6,
-    enumerator >= 0.4.8 && < 0.5,
+    enumerator >= 0.4.13.1 && < 0.5,
     filepath,
     MonadCatchIO-transformers >= 0.2.1 && < 0.3,
     mtl == 2.0.*,
diff --git a/src/Snap/Internal/Types.hs b/src/Snap/Internal/Types.hs
--- a/src/Snap/Internal/Types.hs
+++ b/src/Snap/Internal/Types.hs
@@ -1,20 +1,20 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE EmptyDataDecls #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE PackageImports #-}
-{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE DeriveDataTypeable  #-}
+{-# LANGUAGE EmptyDataDecls      #-}
+{-# LANGUAGE FlexibleInstances   #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE PackageImports      #-}
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module Snap.Internal.Types where
 
 ------------------------------------------------------------------------------
-import "MonadCatchIO-transformers" Control.Monad.CatchIO
-
 import           Blaze.ByteString.Builder
 import           Blaze.ByteString.Builder.Char.Utf8
 import           Control.Applicative
-import           Control.Exception (throwIO, ErrorCall(..))
+import           Control.Exception (SomeException, throwIO, ErrorCall(..))
 import           Control.Monad
+import           Control.Monad.CatchIO
 import           Control.Monad.State
 import           Data.ByteString.Char8 (ByteString)
 import qualified Data.ByteString.Char8 as S
@@ -181,7 +181,11 @@
     catch (Snap m) handler = Snap $ do
         x <- try m
         case x of
-          (Left e)  -> let (Snap z) = handler e in z
+          (Left e)  -> do
+              rethrowIfTermination $ fromException e
+              maybe (throw e)
+                    (\e' -> let (Snap z) = handler e' in z)
+                    (fromException e)
           (Right y) -> return y
 
     block (Snap m) = Snap $ block m
@@ -189,6 +193,14 @@
 
 
 ------------------------------------------------------------------------------
+rethrowIfTermination :: (MonadCatchIO m) =>
+                        Maybe ConnectionTerminatedException ->
+                        m ()
+rethrowIfTermination Nothing  = return ()
+rethrowIfTermination (Just e) = throw e
+
+
+------------------------------------------------------------------------------
 instance MonadPlus Snap where
     mzero = Snap $ return PassOnProcessing
 
@@ -242,14 +254,21 @@
 ------------------------------------------------------------------------------
 -- | Sends the request body through an iteratee (data consumer) and
 -- returns the result.
+--
+-- If the iteratee you pass in here throws an exception, Snap will attempt to
+-- clear the rest of the unread request body before rethrowing the exception.
+-- If your iteratee used 'terminateConnection', however, Snap will give up and
+-- immediately close the socket.
 runRequestBody :: MonadSnap m => Iteratee ByteString IO a -> m a
 runRequestBody iter = do
-    req  <- getRequest
-    senum <- liftIO $ readIORef $ rqBody req
+    bumpTimeout <- liftM ($ 5) getTimeoutAction
+    req         <- getRequest
+    senum       <- liftIO $ readIORef $ rqBody req
     let (SomeEnumerator enum) = senum
 
     -- make sure the iteratee consumes all of the output
-    let iter' = iter >>= \a -> skipToEof >> return a
+    let iter' = handle bumpTimeout req
+                       (iter >>= \a -> skipToEnd bumpTimeout >> return a)
 
     -- run the iteratee
     step   <- liftIO $ runIteratee iter'
@@ -257,12 +276,30 @@
 
     -- stuff a new dummy enumerator into the request, so you can only try to
     -- read the request body from the socket once
-    liftIO $ writeIORef (rqBody req)
-                        (SomeEnumerator $ joinI . take 0 )
-
+    resetEnum req
     return result
 
+  where
+    resetEnum req = liftIO $
+                    writeIORef (rqBody req) $
+                    SomeEnumerator $ joinI . take 0
 
+    skipToEnd bump = killIfTooSlow bump 500 5 skipToEof `catchError` \e ->
+                     throwError $ ConnectionTerminatedException e
+
+    handle bump req =
+        (`catches` [
+          Handler $ \(e :: ConnectionTerminatedException) -> do
+              let en = SomeEnumerator $ const $ throwError e
+              liftIO $ writeIORef (rqBody req) en
+              throwError e
+         , Handler $ \(e :: SomeException) -> do
+              resetEnum req
+              skipToEnd bump
+              throwError e
+         ])
+
+
 ------------------------------------------------------------------------------
 -- | Returns the request body as a bytestring.
 getRequestBody :: MonadSnap m => m L.ByteString
@@ -746,6 +783,27 @@
 
 ------------------------------------------------------------------------------
 instance Exception NoHandlerException
+
+
+------------------------------------------------------------------------------
+data ConnectionTerminatedException = ConnectionTerminatedException SomeException
+  deriving (Typeable)
+
+
+------------------------------------------------------------------------------
+instance Show ConnectionTerminatedException where
+    show (ConnectionTerminatedException e) =
+        "Connection terminated with exception: " ++ show e
+
+
+------------------------------------------------------------------------------
+instance Exception ConnectionTerminatedException
+
+
+------------------------------------------------------------------------------
+-- | Terminate the HTTP session with the given exception.
+terminateConnection :: (Exception e, MonadCatchIO m) => e -> m a
+terminateConnection = throw . ConnectionTerminatedException . toException
 
 
 ------------------------------------------------------------------------------
diff --git a/src/Snap/Iteratee.hs b/src/Snap/Iteratee.hs
--- a/src/Snap/Iteratee.hs
+++ b/src/Snap/Iteratee.hs
@@ -198,7 +198,7 @@
 skipToEof :: (Monad m) => Iteratee a m ()
 skipToEof = continue k
   where
-    k EOF = return ()
+    k EOF = yield () EOF
     k _   = skipToEof
 
 
@@ -724,4 +724,4 @@
 
 ------------------------------------------------------------------------------
 getTime :: IO Double
-getTime = (fromRational . toRational) `fmap` getPOSIXTime
+getTime = realToFrac `fmap` getPOSIXTime
diff --git a/src/Snap/Types.hs b/src/Snap/Types.hs
--- a/src/Snap/Types.hs
+++ b/src/Snap/Types.hs
@@ -17,6 +17,7 @@
   , finishWith
   , catchFinishWith
   , pass
+  , terminateConnection
 
     -- ** Routing
   , method
diff --git a/src/Snap/Util/FileUploads.hs b/src/Snap/Util/FileUploads.hs
--- a/src/Snap/Util/FileUploads.hs
+++ b/src/Snap/Util/FileUploads.hs
@@ -131,8 +131,8 @@
 -- function skips processing using 'pass'.
 --
 -- If the client's upload rate passes below the configured minimum (see
--- 'setMinimumUploadRate' and 'setMinimumUploadSeconds'), this function throws
--- a 'RateTooSlowException'. This setting is there to protect the server
+-- 'setMinimumUploadRate' and 'setMinimumUploadSeconds'), this function
+-- terminates the connection. This setting is there to protect the server
 -- against slowloris-style denial of service attacks.
 --
 -- If the given 'UploadPolicy' stipulates that you wish form inputs to be
@@ -174,15 +174,24 @@
         retVal (_,x) = (partInfo, Right x)
 
         takeIt maxSize = do
+            debug "handleFileUploads/takeIt: begin"
             let it = fmap retVal $
                      joinI' $
+                     iterateeDebugWrapper "takeNoMoreThan" $
                      takeNoMoreThan maxSize $$
                      fileReader uploadedFiles tmpdir partInfo
 
-            it `catches` [ Handler $ \(_ :: TooManyBytesReadException) ->
-                                     (skipToEof >> tooMany maxSize)
-                         , Handler $ \(e :: SomeException) -> throw e
-                         ]
+            it `catches` [
+                    Handler $ \(_ :: TooManyBytesReadException) -> do
+                        debug $ "handleFileUploads/iter: " ++
+                                "caught TooManyBytesReadException"
+                        skipToEof
+                        tooMany maxSize
+                  , Handler $ \(e :: SomeException) -> do
+                        debug $ "handleFileUploads/iter: caught " ++ show e
+                        debug "handleFileUploads/iter: rethrowing"
+                        throw e
+                  ]
 
         tooMany maxSize =
             return ( partInfo
@@ -215,8 +224,8 @@
 -- function skips processing using 'pass'.
 --
 -- If the client's upload rate passes below the configured minimum (see
--- 'setMinimumUploadRate' and 'setMinimumUploadSeconds'), this function throws
--- a 'RateTooSlowException'. This setting is there to protect the server
+-- 'setMinimumUploadRate' and 'setMinimumUploadSeconds'), this function
+-- terminates the connection. This setting is there to protect the server
 -- against slowloris-style denial of service attacks.
 --
 -- If the given 'UploadPolicy' stipulates that you wish form inputs to be
@@ -261,12 +270,21 @@
     procCaptures [] captures
 
   where
+    rateLimit bump m =
+        killIfTooSlow bump
+            (minimumUploadRate uploadPolicy)
+            (minimumUploadSeconds uploadPolicy)
+            m
+          `catchError` \e -> do
+              debug $ "rateLimit: caught " ++ show e
+              let (me::Maybe RateTooSlowException) = fromException e
+              maybe (throwError e)
+                    terminateConnection
+                    me
+
     iter bump boundary ph = iterateeDebugWrapper "killIfTooSlow" $
-                            killIfTooSlow
-                              bump
-                              (minimumUploadRate uploadPolicy)
-                              (minimumUploadSeconds uploadPolicy)
-                              (internalHandleMultipart boundary ph)
+                            rateLimit bump $
+                            internalHandleMultipart boundary ph
 
     ins k v = Map.insertWith' (\a b -> Prelude.head a : b) k [v]
 
@@ -543,10 +561,15 @@
         return $ Capture fieldName var
 
     handler e = do
+        debug $ "captureVariableOrReadFile/handler: caught " ++ show e
         let m = fromException e :: Maybe TooManyBytesReadException
         case m of
-          Nothing -> throwError e
-          Just _  -> throwError $ PolicyViolationException $
+          Nothing -> do
+              debug "didn't expect this error, rethrowing"
+              throwError e
+          Just _  -> do
+              debug "rethrowing as PolicyViolationException"
+              throwError $ PolicyViolationException $
                      T.concat [ "form input '"
                               , TE.decodeUtf8 fieldName
                               , "' exceeded maximum permissible size ("
@@ -566,6 +589,7 @@
            -> PartInfo
            -> Iteratee ByteString IO (PartInfo, FilePath)
 fileReader uploadedFiles tmpdir partInfo = do
+    debug "fileReader: begin"
     (fn, h) <- openFileForUpload uploadedFiles tmpdir
     let i = iterateeDebugWrapper "fileReader" $ iter fn h
     i `catch` \(e::SomeException) -> throwError e
@@ -622,6 +646,7 @@
                iterParser pHeadersWithSeparator
 
         handler e = do
+            debug $ "internalHandleMultipart/takeHeaders: caught " ++ show e
             let m = fromException e :: Maybe TooManyBytesReadException
             case m of
               Nothing -> throwError e
@@ -631,6 +656,7 @@
     --------------------------------------------------------------------------
     iter = do
         hdrs <- takeHeaders
+        debug $ "internalHandleMultipart/iter: got headers"
 
         -- are we using mixed?
         let (contentType, mboundary) = getContentType hdrs
diff --git a/src/Snap/Util/GZip.hs b/src/Snap/Util/GZip.hs
--- a/src/Snap/Util/GZip.hs
+++ b/src/Snap/Util/GZip.hs
@@ -19,6 +19,8 @@
 import           Data.Attoparsec.Char8 hiding (Done)
 import qualified Data.ByteString.Lazy.Char8 as L
 import           Data.ByteString.Char8 (ByteString)
+import qualified Data.ByteString.Char8 as S
+import qualified Data.Char as Char
 import           Data.Maybe
 import qualified Data.Set as Set
 import           Data.Set (Set)
@@ -85,7 +87,7 @@
     -- If a content-encoding is already set, do nothing. This prevents
     -- "withCompression $ withCompression m" from ruining your day.
     when (not $ isJust $ getHeader "Content-Encoding" resp) $ do
-       let mbCt = getHeader "Content-Type" resp
+       let mbCt = fmap chop $ getHeader "Content-Type" resp
 
        debug $ "withCompression', content-type is " ++ show mbCt
 
@@ -97,6 +99,8 @@
     getResponse >>= finishWith
 
   where
+    chop = S.takeWhile (\c -> c /= ';' && not (Char.isSpace c))
+
     chkAcceptEncoding = do
         req <- getRequest
         debug $ "checking accept-encoding"
diff --git a/test/snap-core-testsuite.cabal b/test/snap-core-testsuite.cabal
--- a/test/snap-core-testsuite.cabal
+++ b/test/snap-core-testsuite.cabal
@@ -36,12 +36,12 @@
     dlist >= 0.5 && < 0.6,
     filepath,
     HUnit >= 1.2 && < 2,
-    enumerator >= 0.4.7 && <0.5,
+    enumerator >= 0.4.13.1 && < 0.5,
     MonadCatchIO-transformers >= 0.2 && < 0.3,
     mtl >= 2 && <3,
     old-locale,
     old-time,
-    parallel >= 2.2 && <2.3,
+    parallel >= 3 && <4,
     pureMD5 == 2.1.*,
     regex-posix >= 0.94.4 && <0.95,
     test-framework >= 0.3.1 && <0.4,
diff --git a/test/suite/Snap/Types/Tests.hs b/test/suite/Snap/Types/Tests.hs
--- a/test/suite/Snap/Types/Tests.hs
+++ b/test/suite/Snap/Types/Tests.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE BangPatterns        #-}
 {-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE RankNTypes          #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Snap.Types.Tests
@@ -33,6 +34,7 @@
 import           Snap.Internal.Types
 import           Snap.Internal.Http.Types
 import           Snap.Iteratee
+import qualified Snap.Iteratee as I
 import           Snap.Test.Common
 
 
@@ -42,6 +44,8 @@
         , testEarlyTermination
         , testCatchFinishWith
         , testRqBody
+        , testRqBodyException
+        , testRqBodyTermination
         , testTrivials
         , testMethod
         , testMethods
@@ -60,17 +64,19 @@
         , testBracketSnap ]
 
 
-expectException :: IO () -> IO ()
+expectException :: IO a -> IO ()
 expectException m = do
-    r <- (try m :: IO (Either SomeException ()))
-    let b = either (\e -> show e `using` rdeepseq `seq` True)
-                   (const False) r
+    r <- try m
+    let b = either (\e -> (show (e::SomeException) `using` rdeepseq)
+                              `seq` True)
+                   (const False)
+                   r
     assertBool "expected exception" b
 
 
-expectSpecificException :: Exception e => e -> IO () -> IO ()
+expectSpecificException :: Exception e => e -> IO a -> IO ()
 expectSpecificException e0 m = do
-    r <- (try m :: IO (Either SomeException ()))
+    r <- try m
 
     let b = either (\se -> isJust $
                            forceSameType (Just e0) (fromException se))
@@ -129,13 +135,16 @@
 
 
 mkRqWithBody :: IO Request
-mkRqWithBody = do
-    enum <- newIORef $ SomeEnumerator (enumBS "zazzle" >==> enumEOF)
+mkRqWithBody = mkRqWithEnum (enumBS "zazzle" >==> enumEOF)
+
+
+mkRqWithEnum :: (forall a . Enumerator ByteString IO a) -> IO Request
+mkRqWithEnum e = do
+    enum <- newIORef $ SomeEnumerator e
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False Map.empty
                  enum Nothing GET (1,1) [] "" "/" "/" "/" ""
                  Map.empty
 
-
 testCatchIO :: Test
 testCatchIO = testCase "types/catchIO" $ do
     (_,rsp)  <- go f
@@ -196,6 +205,16 @@
     dummy = const $ return ()
 
 
+goEnum :: (forall a . Enumerator ByteString IO a)
+       -> Snap b
+       -> IO (Request,Response)
+goEnum enum m = do
+    rq <- mkRqWithEnum enum
+    run_ $ runSnap m dummy dummy rq
+  where
+    dummy = const $ return ()
+
+
 testFail :: Test
 testFail = testCase "failure" $ expect404 (go pass)
 
@@ -324,6 +343,39 @@
         getRequestBody >>= liftIO . putMVar mvar2
 
     g = transformRequestBody returnI
+
+
+testRqBodyException :: Test
+testRqBodyException = testCase "types/requestBodyException" $ do
+    (req,resp) <- goEnum (enumList 1 ["the", "quick", "brown", "fox"]) hndlr
+    bd      <- getBody resp
+
+    (SomeEnumerator e) <- readIORef $ rqBody req
+    b' <- liftM (S.concat) $ run_ $ e $$ consume
+    assertEqual "request body was consumed" "" b'
+    assertEqual "response body was produced" "OK" bd
+
+  where
+    h0 = runRequestBody $ do
+             _ <- I.head
+             throw $ ErrorCall "foo"
+
+    hndlr = h0 `catch` \(_::SomeException) -> writeBS "OK"
+
+
+testRqBodyTermination :: Test
+testRqBodyTermination =
+    testCase "types/requestBodyTermination" $
+    expectException $
+    goEnum (enumList 1 ["the", "quick", "brown", "fox"]) hndlr
+
+  where
+    h0 = runRequestBody $ do
+             _ <- I.head
+             terminateConnection $ ErrorCall "foo"
+
+    hndlr = h0 `catch` \(_::SomeException) -> writeBS "OK"
+
 
 
 testTrivials :: Test
diff --git a/test/suite/Snap/Util/FileUploads/Tests.hs b/test/suite/Snap/Util/FileUploads/Tests.hs
--- a/test/suite/Snap/Util/FileUploads/Tests.hs
+++ b/test/suite/Snap/Util/FileUploads/Tests.hs
@@ -264,8 +264,13 @@
 testSlowEnumerator :: Test
 testSlowEnumerator = testCase "fileUploads/tooSlow" $
                      (harness' goSlowEnumerator tmpdir hndl mixedTestBody
-                               `catch` h)
+                               `catch` h0)
   where
+    h0 (e :: ConnectionTerminatedException) =
+        let (ConnectionTerminatedException se) = e
+            (me :: Maybe RateTooSlowException) = fromException se
+        in maybe (throw e) h me
+
     h (e :: RateTooSlowException) = e `seq` return ()
 
     tmpdir = "tempdir_tooslow"
diff --git a/test/suite/Snap/Util/GZip/Tests.hs b/test/suite/Snap/Util/GZip/Tests.hs
--- a/test/suite/Snap/Util/GZip/Tests.hs
+++ b/test/suite/Snap/Util/GZip/Tests.hs
@@ -11,6 +11,7 @@
 import qualified Codec.Compression.Zlib as Zlib
 import           Control.Exception hiding (assert)
 import           Control.Monad (liftM)
+import           Data.ByteString.Char8 (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as L
 import           Data.Digest.Pure.MD5
 import           Data.IORef
@@ -39,6 +40,7 @@
 ------------------------------------------------------------------------------
 tests :: [Test]
 tests = [ testIdentity1
+        , testIdentity1_charset
         , testIdentity2
         , testIdentity3
         , testIdentity4
@@ -157,19 +159,21 @@
 noContentType s = modifyResponse $ setResponseBody $
                   enumBuilder $ fromLazyByteString s
 
+------------------------------------------------------------------------------
+withContentType :: ByteString -> L.ByteString -> Snap ()
+withContentType ct body =
+    modifyResponse $
+    setResponseBody (enumBuilder $ fromLazyByteString body) .
+    setContentType ct
 
 ------------------------------------------------------------------------------
 textPlain :: L.ByteString -> Snap ()
-textPlain s = modifyResponse $
-              setResponseBody (enumBuilder $ fromLazyByteString s) .
-              setContentType "text/plain"
+textPlain = withContentType "text/plain"
 
 
 ------------------------------------------------------------------------------
 binary :: L.ByteString -> Snap ()
-binary s = modifyResponse $
-           setResponseBody (enumBuilder $ fromLazyByteString s) .
-           setContentType "application/octet-stream"
+binary = withContentType "application/octet-stream"
 
 
 ------------------------------------------------------------------------------
@@ -201,7 +205,7 @@
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        -- if there's no content-type, withCompression should be a no-op
+        -- if there's no accept-encoding, withCompression should be a no-op
         (!_,!rsp) <- liftQ $ goNoHeaders (seqSnap $ withCompression
                                                   $ textPlain s)
         assert $ getHeader "Content-Encoding" rsp == Nothing
@@ -221,6 +225,27 @@
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
         (!_,!rsp) <- liftQ $ goGZip (seqSnap $ withCompression $ textPlain s)
+        assert $ getHeader "Content-Encoding" rsp == Just "gzip"
+        assert $ getHeader "Vary" rsp == Just "Accept-Encoding"
+        let body = rspBodyToEnum $ rspBody rsp
+
+        c <- liftQ $
+             runIteratee stream2stream >>= run_ . body
+
+        let s1 = GZip.decompress c
+        assert $ s == s1
+
+
+------------------------------------------------------------------------------
+testIdentity1_charset :: Test
+testIdentity1_charset = testProperty "gzip/identity1_charset" $
+                        monadicIO $ forAllM arbitrary prop
+  where
+    prop :: L.ByteString -> PropertyM IO ()
+    prop s = do
+        (!_,!rsp) <- liftQ $
+                     goGZip (seqSnap $ withCompression $
+                             withContentType "text/plain; charset=utf-8" s)
         assert $ getHeader "Content-Encoding" rsp == Just "gzip"
         assert $ getHeader "Vary" rsp == Just "Accept-Encoding"
         let body = rspBodyToEnum $ rspBody rsp
